Imported Upstream version 0.6.12
[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       if (!di->pool->pos.repodataid && di->pool->pos.solvid == SOLVID_META) {
1931         solvid = SOLVID_META;           /* META pos hack */
1932       } else {
1933         di->data = di->repo->repodata + di->pool->pos.repodataid;
1934         di->repodataid = 0;
1935       }
1936     }
1937   else if (solvid > 0)
1938     {
1939       di->repo = di->pool->solvables[solvid].repo;
1940       di->repoid = 0;
1941     }
1942   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   if (solvid != SOLVID_POS)
1953     di->repodataid = 1;
1954   di->solvid = solvid;
1955   if (solvid)
1956     di->flags |= SEARCH_THISSOLVID;
1957   di->state = di_enterrepo;
1958 }
1959
1960 void
1961 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1962 {
1963   di->nparents = 0;
1964   di->kv.parent = 0;
1965   di->rootlevel = 0;
1966   di->repo = repo;
1967   di->repoid = 0;       /* 0 means stay at repo */
1968   di->repodataid = 1;
1969   di->solvid = 0;
1970   di->flags &= ~SEARCH_THISSOLVID;
1971   di->state = di_enterrepo;
1972 }
1973
1974 int
1975 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1976 {
1977   const char *str;
1978   if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
1979     return 0;
1980   return ma ? datamatcher_match(ma, str) : 1;
1981 }
1982
1983 void
1984 dataiterator_strdup(Dataiterator *di)
1985 {
1986   int l = -1;
1987
1988   if (!di->kv.str || di->kv.str == di->dupstr)
1989     return;
1990   switch (di->key->type)
1991     {
1992     case_CHKSUM_TYPES:
1993     case REPOKEY_TYPE_DIRSTRARRAY:
1994       if (di->kv.num)   /* was it stringified into tmp space? */
1995         l = strlen(di->kv.str) + 1;
1996       break;
1997     default:
1998       break;
1999     }
2000   if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2001     {
2002       switch (di->key->type)
2003         {
2004         case REPOKEY_TYPE_STR:
2005         case REPOKEY_TYPE_DIRSTRARRAY:
2006           l = strlen(di->kv.str) + 1;
2007           break;
2008         case_CHKSUM_TYPES:
2009           l = solv_chksum_len(di->key->type);
2010           break;
2011         case REPOKEY_TYPE_BINARY:
2012           l = di->kv.num;
2013           break;
2014         }
2015     }
2016   if (l >= 0)
2017     {
2018       if (!di->dupstrn || di->dupstrn < l)
2019         {
2020           di->dupstrn = l + 16;
2021           di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
2022         }
2023       if (l)
2024         memcpy(di->dupstr, di->kv.str, l);
2025       di->kv.str = di->dupstr;
2026     }
2027 }
2028
2029 /************************************************************************
2030  * data modify functions
2031  */
2032
2033 /* extend repodata so that it includes solvables p */
2034 void
2035 repodata_extend(Repodata *data, Id p)
2036 {
2037   if (data->start == data->end)
2038     data->start = data->end = p;
2039   if (p >= data->end)
2040     {
2041       int old = data->end - data->start;
2042       int new = p - data->end + 1;
2043       if (data->attrs)
2044         {
2045           data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
2046           memset(data->attrs + old, 0, new * sizeof(Id *));
2047         }
2048       data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
2049       memset(data->incoreoffset + old, 0, new * sizeof(Id));
2050       data->end = p + 1;
2051     }
2052   if (p < data->start)
2053     {
2054       int old = data->end - data->start;
2055       int new = data->start - p;
2056       if (data->attrs)
2057         {
2058           data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
2059           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
2060           memset(data->attrs, 0, new * sizeof(Id *));
2061         }
2062       data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
2063       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
2064       memset(data->incoreoffset, 0, new * sizeof(Id));
2065       data->start = p;
2066     }
2067 }
2068
2069 /* shrink end of repodata */
2070 void
2071 repodata_shrink(Repodata *data, int end)
2072 {
2073   int i;
2074
2075   if (data->end <= end)
2076     return;
2077   if (data->start >= end)
2078     {
2079       if (data->attrs)
2080         {
2081           for (i = 0; i < data->end - data->start; i++)
2082             solv_free(data->attrs[i]);
2083           data->attrs = solv_free(data->attrs);
2084         }
2085       data->incoreoffset = solv_free(data->incoreoffset);
2086       data->start = data->end = 0;
2087       return;
2088     }
2089   if (data->attrs)
2090     {
2091       for (i = end; i < data->end; i++)
2092         solv_free(data->attrs[i - data->start]);
2093       data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
2094     }
2095   if (data->incoreoffset)
2096     data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
2097   data->end = end;
2098 }
2099
2100 /* extend repodata so that it includes solvables from start to start + num - 1 */
2101 void
2102 repodata_extend_block(Repodata *data, Id start, Id num)
2103 {
2104   if (!num)
2105     return;
2106   if (!data->incoreoffset)
2107     {
2108       /* this also means that data->attrs is NULL */
2109       data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
2110       data->start = start;
2111       data->end = start + num;
2112       return;
2113     }
2114   repodata_extend(data, start);
2115   if (num > 1)
2116     repodata_extend(data, start + num - 1);
2117 }
2118
2119 /**********************************************************************/
2120
2121
2122 #define REPODATA_ATTRS_BLOCK 31
2123 #define REPODATA_ATTRDATA_BLOCK 1023
2124 #define REPODATA_ATTRIDDATA_BLOCK 63
2125 #define REPODATA_ATTRNUM64DATA_BLOCK 15
2126
2127
2128 Id
2129 repodata_new_handle(Repodata *data)
2130 {
2131   if (!data->nxattrs)
2132     {
2133       data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2134       data->nxattrs = 2;        /* -1: SOLVID_META */
2135     }
2136   data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
2137   data->xattrs[data->nxattrs] = 0;
2138   return -(data->nxattrs++);
2139 }
2140
2141 static inline Id **
2142 repodata_get_attrp(Repodata *data, Id handle)
2143 {
2144   if (handle < 0)
2145     {
2146       if (handle == SOLVID_META && !data->xattrs)
2147         {
2148           data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2149           data->nxattrs = 2;
2150         }
2151       return data->xattrs - handle;
2152     }
2153   if (handle < data->start || handle >= data->end)
2154     repodata_extend(data, handle);
2155   if (!data->attrs)
2156     data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
2157   return data->attrs + (handle - data->start);
2158 }
2159
2160 static void
2161 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
2162 {
2163   Id *pp;
2164   Id *ap, **app;
2165   int i;
2166
2167   app = repodata_get_attrp(data, handle);
2168   ap = *app;
2169   i = 0;
2170   if (ap)
2171     {
2172       /* Determine equality based on the name only, allows us to change
2173          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
2174       for (pp = ap; *pp; pp += 2)
2175         if (data->keys[*pp].name == data->keys[keyid].name)
2176           break;
2177       if (*pp)
2178         {
2179           if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
2180             {
2181               pp[0] = keyid;
2182               pp[1] = val;
2183             }
2184           return;
2185         }
2186       i = pp - ap;
2187     }
2188   ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
2189   *app = ap;
2190   pp = ap + i;
2191   *pp++ = keyid;
2192   *pp++ = val;
2193   *pp = 0;
2194 }
2195
2196
2197 static void
2198 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
2199 {
2200   Id keyid;
2201
2202   keyid = repodata_key2id(data, key, 1);
2203   repodata_insert_keyid(data, solvid, keyid, val, 1);
2204 }
2205
2206 void
2207 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
2208 {
2209   Repokey key;
2210   key.name = keyname;
2211   key.type = REPOKEY_TYPE_ID;
2212   key.size = 0;
2213   key.storage = KEY_STORAGE_INCORE;
2214   repodata_set(data, solvid, &key, id);
2215 }
2216
2217 void
2218 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
2219 {
2220   Repokey key;
2221   key.name = keyname;
2222   key.type = REPOKEY_TYPE_NUM;
2223   key.size = 0;
2224   key.storage = KEY_STORAGE_INCORE;
2225   if (num >= 0x80000000)
2226     {
2227       data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
2228       data->attrnum64data[data->attrnum64datalen] = num;
2229       num = 0x80000000 | data->attrnum64datalen++;
2230     }
2231   repodata_set(data, solvid, &key, (Id)num);
2232 }
2233
2234 void
2235 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
2236 {
2237   Repokey key;
2238   Id id;
2239   if (data->localpool)
2240     id = stringpool_str2id(&data->spool, str, 1);
2241   else
2242     id = pool_str2id(data->repo->pool, str, 1);
2243   key.name = keyname;
2244   key.type = REPOKEY_TYPE_ID;
2245   key.size = 0;
2246   key.storage = KEY_STORAGE_INCORE;
2247   repodata_set(data, solvid, &key, id);
2248 }
2249
2250 void
2251 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
2252 {
2253   Repokey key;
2254   key.name = keyname;
2255   key.type = REPOKEY_TYPE_CONSTANT;
2256   key.size = constant;
2257   key.storage = KEY_STORAGE_INCORE;
2258   repodata_set(data, solvid, &key, 0);
2259 }
2260
2261 void
2262 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
2263 {
2264   Repokey key;
2265   key.name = keyname;
2266   key.type = REPOKEY_TYPE_CONSTANTID;
2267   key.size = id;
2268   key.storage = KEY_STORAGE_INCORE;
2269   repodata_set(data, solvid, &key, 0);
2270 }
2271
2272 void
2273 repodata_set_void(Repodata *data, Id solvid, Id keyname)
2274 {
2275   Repokey key;
2276   key.name = keyname;
2277   key.type = REPOKEY_TYPE_VOID;
2278   key.size = 0;
2279   key.storage = KEY_STORAGE_INCORE;
2280   repodata_set(data, solvid, &key, 0);
2281 }
2282
2283 void
2284 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
2285 {
2286   Repokey key;
2287   int l;
2288
2289   l = strlen(str) + 1;
2290   key.name = keyname;
2291   key.type = REPOKEY_TYPE_STR;
2292   key.size = 0;
2293   key.storage = KEY_STORAGE_INCORE;
2294   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2295   memcpy(data->attrdata + data->attrdatalen, str, l);
2296   repodata_set(data, solvid, &key, data->attrdatalen);
2297   data->attrdatalen += l;
2298 }
2299
2300 void
2301 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
2302 {
2303   Repokey key;
2304   unsigned char *dp;
2305
2306   if (len < 0)
2307     return;
2308   key.name = keyname;
2309   key.type = REPOKEY_TYPE_BINARY;
2310   key.size = 0;
2311   key.storage = KEY_STORAGE_INCORE;
2312   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
2313   dp = data->attrdata + data->attrdatalen;
2314   if (len >= (1 << 14))
2315     {
2316       if (len >= (1 << 28))
2317         *dp++ = (len >> 28) | 128;
2318       if (len >= (1 << 21))
2319         *dp++ = (len >> 21) | 128;
2320       *dp++ = (len >> 14) | 128;
2321     }
2322   if (len >= (1 << 7))
2323     *dp++ = (len >> 7) | 128;
2324   *dp++ = len & 127;
2325   if (len)
2326     memcpy(dp, buf, len);
2327   repodata_set(data, solvid, &key, data->attrdatalen);
2328   data->attrdatalen = dp + len - data->attrdata;
2329 }
2330
2331 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
2332  * so that the caller can append entrysize new elements plus the termination zero there */
2333 static void
2334 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2335 {
2336   int oldsize;
2337   Id *ida, *pp, **ppp;
2338
2339   /* check if it is the same as last time, this speeds things up a lot */
2340   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2341     {
2342       /* great! just append the new data */
2343       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2344       data->attriddatalen--;    /* overwrite terminating 0  */
2345       data->lastdatalen += entrysize;
2346       return;
2347     }
2348
2349   ppp = repodata_get_attrp(data, handle);
2350   pp = *ppp;
2351   if (pp)
2352     {
2353       for (; *pp; pp += 2)
2354         if (data->keys[*pp].name == keyname)
2355           break;
2356     }
2357   if (!pp || !*pp || data->keys[*pp].type != keytype)
2358     {
2359       /* not found. allocate new key */
2360       Repokey key;
2361       Id keyid;
2362       key.name = keyname;
2363       key.type = keytype;
2364       key.size = 0;
2365       key.storage = KEY_STORAGE_INCORE;
2366       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2367       keyid = repodata_key2id(data, &key, 1);
2368       repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
2369       data->lasthandle = handle;
2370       data->lastkey = keyid;
2371       data->lastdatalen = data->attriddatalen + entrysize + 1;
2372       return;
2373     }
2374   oldsize = 0;
2375   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2376     oldsize += entrysize;
2377   if (ida + 1 == data->attriddata + data->attriddatalen)
2378     {
2379       /* this was the last entry, just append it */
2380       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2381       data->attriddatalen--;    /* overwrite terminating 0  */
2382     }
2383   else
2384     {
2385       /* too bad. move to back. */
2386       data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2387       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2388       pp[1] = data->attriddatalen;
2389       data->attriddatalen += oldsize;
2390     }
2391   data->lasthandle = handle;
2392   data->lastkey = *pp;
2393   data->lastdatalen = data->attriddatalen + entrysize + 1;
2394 }
2395
2396 void
2397 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2398                       const unsigned char *str)
2399 {
2400   Repokey key;
2401   int l;
2402
2403   if (!(l = solv_chksum_len(type)))
2404     return;
2405   key.name = keyname;
2406   key.type = type;
2407   key.size = 0;
2408   key.storage = KEY_STORAGE_INCORE;
2409   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2410   memcpy(data->attrdata + data->attrdatalen, str, l);
2411   repodata_set(data, solvid, &key, data->attrdatalen);
2412   data->attrdatalen += l;
2413 }
2414
2415 void
2416 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2417                       const char *str)
2418 {
2419   unsigned char buf[64];
2420   int l;
2421
2422   if (!(l = solv_chksum_len(type)))
2423     return;
2424   if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
2425     return;
2426   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2427 }
2428
2429 const char *
2430 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2431 {
2432   int l;
2433
2434   if (!(l = solv_chksum_len(type)))
2435     return "";
2436   return pool_bin2hex(data->repo->pool, buf, l);
2437 }
2438
2439 /* rpm filenames don't contain the epoch, so strip it */
2440 static inline const char *
2441 evrid2vrstr(Pool *pool, Id evrid)
2442 {
2443   const char *p, *evr = pool_id2str(pool, evrid);
2444   if (!evr)
2445     return evr;
2446   for (p = evr; *p >= '0' && *p <= '9'; p++)
2447     ;
2448   return p != evr && *p == ':' && p[1] ? p + 1 : evr;
2449 }
2450
2451 static inline void
2452 repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2453 {
2454   Id id;
2455   if (data->localpool)
2456     id = stringpool_strn2id(&data->spool, str, l, 1);
2457   else
2458     id = pool_strn2id(data->repo->pool, str, l, 1);
2459   repodata_set_id(data, solvid, keyname, id);
2460 }
2461
2462 static inline void
2463 repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2464 {
2465   if (!str[l])
2466     repodata_set_str(data, solvid, keyname, str);
2467   else
2468     {
2469       char *s = solv_strdup(str);
2470       s[l] = 0;
2471       repodata_set_str(data, solvid, keyname, s);
2472       free(s);
2473     }
2474 }
2475
2476 void
2477 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2478 {
2479   Pool *pool = data->repo->pool;
2480   Solvable *s;
2481   const char *str, *fp;
2482   int l = 0;
2483
2484   if (medianr)
2485     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2486   if (!dir)
2487     {
2488       if ((dir = strrchr(file, '/')) != 0)
2489         {
2490           l = dir - file;
2491           dir = file;
2492           file = dir + l + 1;
2493           if (!l)
2494             l++;
2495         }
2496     }
2497   else
2498     l = strlen(dir);
2499   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2500     {
2501       dir += 2;
2502       l -= 2;
2503     }
2504   if (l == 1 && dir[0] == '.')
2505     l = 0;
2506   s = pool->solvables + solvid;
2507   if (dir && l)
2508     {
2509       str = pool_id2str(pool, s->arch);
2510       if (!strncmp(dir, str, l) && !str[l])
2511         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2512       else
2513         repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
2514     }
2515   fp = file;
2516   str = pool_id2str(pool, s->name);
2517   l = strlen(str);
2518   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2519     {
2520       fp += l + 1;
2521       str = evrid2vrstr(pool, s->evr);
2522       l = strlen(str);
2523       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2524         {
2525           fp += l + 1;
2526           str = pool_id2str(pool, s->arch);
2527           l = strlen(str);
2528           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2529             {
2530               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2531               return;
2532             }
2533         }
2534     }
2535   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2536 }
2537
2538 /* XXX: medianr is currently not stored */
2539 void
2540 repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
2541 {
2542   int l = 0;
2543   const char *evr, *suf, *s;
2544
2545   if (!dir)
2546     {
2547       if ((dir = strrchr(file, '/')) != 0)
2548         {
2549           l = dir - file;
2550           dir = file;
2551           file = dir + l + 1;
2552           if (!l)
2553             l++;
2554         }
2555     }
2556   else
2557     l = strlen(dir);
2558   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2559     {
2560       dir += 2;
2561       l -= 2;
2562     }
2563   if (l == 1 && dir[0] == '.')
2564     l = 0;
2565   if (dir && l)
2566     repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
2567   evr = strchr(file, '-');
2568   if (evr)
2569     {
2570       for (s = evr - 1; s > file; s--)
2571         if (*s == '-')
2572           {
2573             evr = s;
2574             break;
2575           }
2576     }
2577   suf = strrchr(file, '.');
2578   if (suf)
2579     {
2580       for (s = suf - 1; s > file; s--)
2581         if (*s == '.')
2582           {
2583             suf = s;
2584             break;
2585           }
2586       if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
2587         {
2588           /* We accept one more item as suffix.  */
2589           for (s = suf - 1; s > file; s--)
2590             if (*s == '.')
2591               {
2592                 suf = s;
2593                 break;
2594               }
2595         }
2596     }
2597   if (!evr)
2598     suf = 0;
2599   if (suf && evr && suf < evr)
2600     suf = 0;
2601   repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
2602   if (evr)
2603     repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
2604   if (suf)
2605     repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
2606 }
2607
2608 void
2609 repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
2610 {
2611   Pool *pool = data->repo->pool;
2612   Solvable *s = pool->solvables + solvid;
2613   const char *p, *sevr, *sarch, *name, *evr;
2614
2615   p = strrchr(sourcepkg, '.');
2616   if (!p || strcmp(p, ".rpm") != 0)
2617     {
2618       if (*sourcepkg)
2619         repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
2620       return;
2621     }
2622   p--;
2623   while (p > sourcepkg && *p != '.')
2624     p--;
2625   if (*p != '.' || p == sourcepkg)
2626     return;
2627   sarch = p-- + 1;
2628   while (p > sourcepkg && *p != '-')
2629     p--;
2630   if (*p != '-' || p == sourcepkg)
2631     return;
2632   p--;
2633   while (p > sourcepkg && *p != '-')
2634     p--;
2635   if (*p != '-' || p == sourcepkg)
2636     return;
2637   sevr = p + 1;
2638   pool = s->repo->pool;
2639
2640   name = pool_id2str(pool, s->name);
2641   if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
2642     repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
2643   else
2644     repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
2645
2646   evr = evrid2vrstr(pool, s->evr);
2647   if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
2648     repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
2649   else
2650     repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
2651
2652   if (!strcmp(sarch, "src.rpm"))
2653     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
2654   else if (!strcmp(sarch, "nosrc.rpm"))
2655     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
2656   else
2657     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
2658 }
2659
2660 void
2661 repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
2662 {
2663   Repokey key;
2664   int i;
2665
2666   key.name = keyname;
2667   key.type = REPOKEY_TYPE_IDARRAY;
2668   key.size = 0;
2669   key.storage = KEY_STORAGE_INCORE;
2670   repodata_set(data, solvid, &key, data->attriddatalen);
2671   data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2672   for (i = 0; i < q->count; i++)
2673     data->attriddata[data->attriddatalen++] = q->elements[i];
2674   data->attriddata[data->attriddatalen++] = 0;
2675 }
2676
2677 void
2678 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2679 {
2680   assert(dir);
2681 #if 0
2682 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2683 #endif
2684   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2685   data->attriddata[data->attriddatalen++] = dir;
2686   data->attriddata[data->attriddatalen++] = num;
2687   data->attriddata[data->attriddatalen++] = num2;
2688   data->attriddata[data->attriddatalen++] = 0;
2689 }
2690
2691 void
2692 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2693 {
2694   Id stroff;
2695   int l;
2696
2697   assert(dir);
2698   l = strlen(str) + 1;
2699   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2700   memcpy(data->attrdata + data->attrdatalen, str, l);
2701   stroff = data->attrdatalen;
2702   data->attrdatalen += l;
2703
2704 #if 0
2705 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2706 #endif
2707   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2708   data->attriddata[data->attriddatalen++] = dir;
2709   data->attriddata[data->attriddatalen++] = stroff;
2710   data->attriddata[data->attriddatalen++] = 0;
2711 }
2712
2713 void
2714 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2715 {
2716 #if 0
2717 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2718 #endif
2719   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2720   data->attriddata[data->attriddatalen++] = id;
2721   data->attriddata[data->attriddatalen++] = 0;
2722 }
2723
2724 void
2725 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2726                            const char *str)
2727 {
2728   Id id;
2729   if (data->localpool)
2730     id = stringpool_str2id(&data->spool, str, 1);
2731   else
2732     id = pool_str2id(data->repo->pool, str, 1);
2733   repodata_add_idarray(data, solvid, keyname, id);
2734 }
2735
2736 void
2737 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2738 {
2739   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2740   data->attriddata[data->attriddatalen++] = ghandle;
2741   data->attriddata[data->attriddatalen++] = 0;
2742 }
2743
2744 void
2745 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2746 {
2747   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2748   data->attriddata[data->attriddatalen++] = ghandle;
2749   data->attriddata[data->attriddatalen++] = 0;
2750 }
2751
2752 void
2753 repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
2754 {
2755   Id *pp, *ap, **app;
2756   app = repodata_get_attrp(data, solvid);
2757   ap = *app;
2758   if (!ap)
2759     return;
2760   for (; *ap; ap += 2)
2761     if (data->keys[*ap].name == keyname)
2762       break;
2763   if (!*ap)
2764     return;
2765   pp = ap;
2766   ap += 2;
2767   for (; *ap; ap += 2)
2768     {
2769       if (data->keys[*ap].name == keyname)
2770         continue;
2771       *pp++ = ap[0];
2772       *pp++ = ap[1];
2773     }
2774   *pp = 0;
2775 }
2776
2777 /* XXX: does not work correctly, needs fix in iterators! */
2778 void
2779 repodata_unset(Repodata *data, Id solvid, Id keyname)
2780 {
2781   Repokey key;
2782   key.name = keyname;
2783   key.type = REPOKEY_TYPE_DELETED;
2784   key.size = 0;
2785   key.storage = KEY_STORAGE_INCORE;
2786   repodata_set(data, solvid, &key, 0);
2787 }
2788
2789 /* add all (uninternalized) attrs from src to dest */
2790 void
2791 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2792 {
2793   Id *keyp;
2794   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2795     return;
2796   for (; *keyp; keyp += 2)
2797     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2798 }
2799
2800 /* add some (uninternalized) attrs from src to dest */
2801 void
2802 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2803 {
2804   Id *keyp;
2805   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2806     return;
2807   for (; *keyp; keyp += 2)
2808     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2809       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2810 }
2811
2812 /* swap (uninternalized) attrs from src and dest */
2813 void
2814 repodata_swap_attrs(Repodata *data, Id dest, Id src)
2815 {
2816   Id *tmpattrs;
2817   if (!data->attrs || dest == src)
2818     return;
2819   if (dest < data->start || dest >= data->end)
2820     repodata_extend(data, dest);
2821   if (src < data->start || src >= data->end)
2822     repodata_extend(data, src);
2823   tmpattrs = data->attrs[dest - data->start];
2824   data->attrs[dest - data->start] = data->attrs[src - data->start];
2825   data->attrs[src - data->start] = tmpattrs;
2826 }
2827
2828
2829 /**********************************************************************/
2830
2831 /* TODO: unify with repo_write and repo_solv! */
2832
2833 #define EXTDATA_BLOCK 1023
2834
2835 struct extdata {
2836   unsigned char *buf;
2837   int len;
2838 };
2839
2840 static void
2841 data_addid(struct extdata *xd, Id sx)
2842 {
2843   unsigned int x = (unsigned int)sx;
2844   unsigned char *dp;
2845
2846   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2847   dp = xd->buf + xd->len;
2848
2849   if (x >= (1 << 14))
2850     {
2851       if (x >= (1 << 28))
2852         *dp++ = (x >> 28) | 128;
2853       if (x >= (1 << 21))
2854         *dp++ = (x >> 21) | 128;
2855       *dp++ = (x >> 14) | 128;
2856     }
2857   if (x >= (1 << 7))
2858     *dp++ = (x >> 7) | 128;
2859   *dp++ = x & 127;
2860   xd->len = dp - xd->buf;
2861 }
2862
2863 static void
2864 data_addid64(struct extdata *xd, unsigned long long x)
2865 {
2866   if (x >= 0x100000000)
2867     {
2868       if ((x >> 35) != 0)
2869         {
2870           data_addid(xd, (Id)(x >> 35));
2871           xd->buf[xd->len - 1] |= 128;
2872         }
2873       data_addid(xd, (Id)((unsigned int)x | 0x80000000));
2874       xd->buf[xd->len - 5] = (x >> 28) | 128;
2875     }
2876   else
2877     data_addid(xd, (Id)x);
2878 }
2879
2880 static void
2881 data_addideof(struct extdata *xd, Id sx, int eof)
2882 {
2883   unsigned int x = (unsigned int)sx;
2884   unsigned char *dp;
2885
2886   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2887   dp = xd->buf + xd->len;
2888
2889   if (x >= (1 << 13))
2890     {
2891       if (x >= (1 << 27))
2892         *dp++ = (x >> 27) | 128;
2893       if (x >= (1 << 20))
2894         *dp++ = (x >> 20) | 128;
2895       *dp++ = (x >> 13) | 128;
2896     }
2897   if (x >= (1 << 6))
2898     *dp++ = (x >> 6) | 128;
2899   *dp++ = eof ? (x & 63) : (x & 63) | 64;
2900   xd->len = dp - xd->buf;
2901 }
2902
2903 static void
2904 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2905 {
2906   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2907   memcpy(xd->buf + xd->len, blob, len);
2908   xd->len += len;
2909 }
2910
2911 /*********************************/
2912
2913 /* this is to reduct memory usage when internalizing oversized repos */
2914 static void
2915 compact_attrdata(Repodata *data, int entry, int nentry)
2916 {
2917   int i;
2918   unsigned int attrdatastart = data->attrdatalen;
2919   unsigned int attriddatastart = data->attriddatalen;
2920   if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
2921     return;
2922   for (i = entry; i < nentry; i++)
2923     {
2924       Id v, *attrs = data->attrs[i];
2925       if (!attrs)
2926         continue;
2927       for (; *attrs; attrs += 2)
2928         {
2929           switch (data->keys[*attrs].type)
2930             {
2931             case REPOKEY_TYPE_STR:
2932             case REPOKEY_TYPE_BINARY:
2933             case_CHKSUM_TYPES:
2934               if ((unsigned int)attrs[1] < attrdatastart)
2935                  attrdatastart = attrs[1];
2936               break;
2937             case REPOKEY_TYPE_DIRSTRARRAY:
2938               for (v = attrs[1]; data->attriddata[v] ; v += 2)
2939                 if (data->attriddata[v + 1] < attrdatastart)
2940                   attrdatastart = data->attriddata[v + 1];
2941               /* FALLTHROUGH */
2942             case REPOKEY_TYPE_IDARRAY:
2943             case REPOKEY_TYPE_DIRNUMNUMARRAY:
2944               if ((unsigned int)attrs[1] < attriddatastart)
2945                 attriddatastart = attrs[1];
2946               break;
2947             case REPOKEY_TYPE_FIXARRAY:
2948             case REPOKEY_TYPE_FLEXARRAY:
2949               return;
2950             default:
2951               break;
2952             }
2953         }
2954     }
2955 #if 0
2956   printf("compact_attrdata %d %d\n", entry, nentry);
2957   printf("attrdatastart: %d\n", attrdatastart);
2958   printf("attriddatastart: %d\n", attriddatastart);
2959 #endif
2960   if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
2961     return;
2962   for (i = entry; i < nentry; i++)
2963     {
2964       Id v, *attrs = data->attrs[i];
2965       if (!attrs)
2966         continue;
2967       for (; *attrs; attrs += 2)
2968         {
2969           switch (data->keys[*attrs].type)
2970             {
2971             case REPOKEY_TYPE_STR:
2972             case REPOKEY_TYPE_BINARY:
2973             case_CHKSUM_TYPES:
2974               attrs[1] -= attrdatastart;
2975               break;
2976             case REPOKEY_TYPE_DIRSTRARRAY:
2977               for (v = attrs[1]; data->attriddata[v] ; v += 2)
2978                 data->attriddata[v + 1] -= attrdatastart;
2979               /* FALLTHROUGH */
2980             case REPOKEY_TYPE_IDARRAY:
2981             case REPOKEY_TYPE_DIRNUMNUMARRAY:
2982               attrs[1] -= attriddatastart;
2983               break;
2984             default:
2985               break;
2986             }
2987         }
2988     }
2989   if (attrdatastart)
2990     {
2991       data->attrdatalen -= attrdatastart;
2992       memmove(data->attrdata, data->attrdata + attrdatastart, data->attrdatalen);
2993       data->attrdata = solv_extend_resize(data->attrdata, data->attrdatalen, 1, REPODATA_ATTRDATA_BLOCK);
2994     }
2995   if (attriddatastart)
2996     {
2997       data->attriddatalen -= attriddatastart;
2998       memmove(data->attriddata, data->attriddata + attriddatastart, data->attriddatalen * sizeof(Id));
2999       data->attriddata = solv_extend_resize(data->attriddata, data->attriddatalen, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
3000     }
3001 }
3002
3003 /* internalalize some key into incore/vincore data */
3004
3005 static void
3006 repodata_serialize_key(Repodata *data, struct extdata *newincore,
3007                        struct extdata *newvincore,
3008                        Id *schema,
3009                        Repokey *key, Id val)
3010 {
3011   Id *ida;
3012   struct extdata *xd;
3013   unsigned int oldvincorelen = 0;
3014   Id schemaid, *sp;
3015
3016   xd = newincore;
3017   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3018     {
3019       xd = newvincore;
3020       oldvincorelen = xd->len;
3021     }
3022   switch (key->type)
3023     {
3024     case REPOKEY_TYPE_VOID:
3025     case REPOKEY_TYPE_CONSTANT:
3026     case REPOKEY_TYPE_CONSTANTID:
3027       break;
3028     case REPOKEY_TYPE_STR:
3029       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
3030       break;
3031     case REPOKEY_TYPE_MD5:
3032       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
3033       break;
3034     case REPOKEY_TYPE_SHA1:
3035       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
3036       break;
3037     case REPOKEY_TYPE_SHA224:
3038       data_addblob(xd, data->attrdata + val, SIZEOF_SHA224);
3039       break;
3040     case REPOKEY_TYPE_SHA256:
3041       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
3042       break;
3043     case REPOKEY_TYPE_SHA384:
3044       data_addblob(xd, data->attrdata + val, SIZEOF_SHA384);
3045       break;
3046     case REPOKEY_TYPE_SHA512:
3047       data_addblob(xd, data->attrdata + val, SIZEOF_SHA512);
3048       break;
3049     case REPOKEY_TYPE_NUM:
3050       if (val & 0x80000000)
3051         {
3052           data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
3053           break;
3054         }
3055       /* FALLTHROUGH */
3056     case REPOKEY_TYPE_ID:
3057     case REPOKEY_TYPE_DIR:
3058       data_addid(xd, val);
3059       break;
3060     case REPOKEY_TYPE_BINARY:
3061       {
3062         Id len;
3063         unsigned char *dp = data_read_id(data->attrdata + val, &len);
3064         dp += (unsigned int)len;
3065         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
3066       }
3067       break;
3068     case REPOKEY_TYPE_IDARRAY:
3069       for (ida = data->attriddata + val; *ida; ida++)
3070         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
3071       break;
3072     case REPOKEY_TYPE_DIRNUMNUMARRAY:
3073       for (ida = data->attriddata + val; *ida; ida += 3)
3074         {
3075           data_addid(xd, ida[0]);
3076           data_addid(xd, ida[1]);
3077           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
3078         }
3079       break;
3080     case REPOKEY_TYPE_DIRSTRARRAY:
3081       for (ida = data->attriddata + val; *ida; ida += 2)
3082         {
3083           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
3084           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
3085         }
3086       break;
3087     case REPOKEY_TYPE_FIXARRAY:
3088       {
3089         int num = 0;
3090         schemaid = 0;
3091         for (ida = data->attriddata + val; *ida; ida++)
3092           {
3093             Id *kp;
3094             sp = schema;
3095             kp = data->xattrs[-*ida];
3096             if (!kp)
3097               continue;
3098             num++;
3099             for (;*kp; kp += 2)
3100               *sp++ = *kp;
3101             *sp = 0;
3102             if (!schemaid)
3103               schemaid = repodata_schema2id(data, schema, 1);
3104             else if (schemaid != repodata_schema2id(data, schema, 0))
3105               {
3106                 pool_debug(data->repo->pool, SOLV_FATAL, "fixarray substructs with different schemas\n");
3107                 exit(1);
3108               }
3109           }
3110         if (!num)
3111           break;
3112         data_addid(xd, num);
3113         data_addid(xd, schemaid);
3114         for (ida = data->attriddata + val; *ida; ida++)
3115           {
3116             Id *kp = data->xattrs[-*ida];
3117             if (!kp)
3118               continue;
3119             for (;*kp; kp += 2)
3120               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
3121           }
3122         break;
3123       }
3124     case REPOKEY_TYPE_FLEXARRAY:
3125       {
3126         int num = 0;
3127         for (ida = data->attriddata + val; *ida; ida++)
3128           num++;
3129         data_addid(xd, num);
3130         for (ida = data->attriddata + val; *ida; ida++)
3131           {
3132             Id *kp = data->xattrs[-*ida];
3133             if (!kp)
3134               {
3135                 data_addid(xd, 0);      /* XXX */
3136                 continue;
3137               }
3138             sp = schema;
3139             for (;*kp; kp += 2)
3140               *sp++ = *kp;
3141             *sp = 0;
3142             schemaid = repodata_schema2id(data, schema, 1);
3143             data_addid(xd, schemaid);
3144             kp = data->xattrs[-*ida];
3145             for (;*kp; kp += 2)
3146               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
3147           }
3148         break;
3149       }
3150     default:
3151       pool_debug(data->repo->pool, SOLV_FATAL, "don't know how to handle type %d\n", key->type);
3152       exit(1);
3153     }
3154   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3155     {
3156       /* put offset/len in incore */
3157       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
3158       oldvincorelen = xd->len - oldvincorelen;
3159       data_addid(newincore, oldvincorelen);
3160     }
3161 }
3162
3163 void
3164 repodata_internalize(Repodata *data)
3165 {
3166   Repokey *key, solvkey;
3167   Id entry, nentry;
3168   Id schemaid, keyid, *schema, *sp, oldschema, *keyp, *seen;
3169   int schemaidx;
3170   unsigned char *dp, *ndp;
3171   int newschema, oldcount;
3172   struct extdata newincore;
3173   struct extdata newvincore;
3174   Id solvkeyid;
3175
3176   if (!data->attrs && !data->xattrs)
3177     return;
3178
3179 #if 0
3180   printf("repodata_internalize %d\n", data->repodataid);
3181   printf("  attr data: %d K\n", data->attrdatalen / 1024);
3182   printf("  attrid data: %d K\n", data->attriddatalen / (1024 / 4));
3183 #endif
3184   newvincore.buf = data->vincore;
3185   newvincore.len = data->vincorelen;
3186
3187   /* find the solvables key, create if needed */
3188   memset(&solvkey, 0, sizeof(solvkey));
3189   solvkey.name = REPOSITORY_SOLVABLES;
3190   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
3191   solvkey.size = 0;
3192   solvkey.storage = KEY_STORAGE_INCORE;
3193   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
3194
3195   schema = solv_malloc2(data->nkeys, sizeof(Id));
3196   seen = solv_malloc2(data->nkeys, sizeof(Id));
3197
3198   /* Merge the data already existing (in data->schemata, ->incoredata and
3199      friends) with the new attributes in data->attrs[].  */
3200   nentry = data->end - data->start;
3201   memset(&newincore, 0, sizeof(newincore));
3202   data_addid(&newincore, 0);    /* start data at offset 1 */
3203
3204   data->mainschema = 0;
3205   data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
3206
3207   /* join entry data */
3208   /* we start with the meta data, entry -1 */
3209   for (entry = -1; entry < nentry; entry++)
3210     {
3211       memset(seen, 0, data->nkeys * sizeof(Id));
3212       oldschema = 0;
3213       dp = data->incoredata;
3214       if (dp)
3215         {
3216           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
3217           dp = data_read_id(dp, &oldschema);
3218         }
3219 #if 0
3220 fprintf(stderr, "oldschema %d\n", oldschema);
3221 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
3222 fprintf(stderr, "schemadata %p\n", data->schemadata);
3223 #endif
3224       /* seen: -1: old data  0: skipped  >0: id + 1 */
3225       newschema = 0;
3226       oldcount = 0;
3227       sp = schema;
3228       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
3229         {
3230           if (seen[*keyp])
3231             {
3232               pool_debug(data->repo->pool, SOLV_FATAL, "Inconsistent old data (key occured twice).\n");
3233               exit(1);
3234             }
3235           seen[*keyp] = -1;
3236           *sp++ = *keyp;
3237           oldcount++;
3238         }
3239       if (entry >= 0)
3240         keyp = data->attrs ? data->attrs[entry] : 0;
3241       else
3242         {
3243           /* strip solvables key */
3244           *sp = 0;
3245           for (sp = keyp = schema; *sp; sp++)
3246             if (*sp != solvkeyid)
3247               *keyp++ = *sp;
3248             else
3249               oldcount--;
3250           sp = keyp;
3251           seen[solvkeyid] = 0;
3252           keyp = data->xattrs ? data->xattrs[1] : 0;
3253         }
3254       if (keyp)
3255         for (; *keyp; keyp += 2)
3256           {
3257             if (!seen[*keyp])
3258               {
3259                 newschema = 1;
3260                 *sp++ = *keyp;
3261               }
3262             seen[*keyp] = keyp[1] + 1;
3263           }
3264       if (entry < 0 && data->end != data->start)
3265         {
3266           *sp++ = solvkeyid;
3267           newschema = 1;
3268         }
3269       *sp = 0;
3270       if (newschema)
3271         /* Ideally we'd like to sort the new schema here, to ensure
3272            schema equality independend of the ordering.  We can't do that
3273            yet.  For once see below (old ids need to come before new ids).
3274            An additional difficulty is that we also need to move
3275            the values with the keys.  */
3276         schemaid = repodata_schema2id(data, schema, 1);
3277       else
3278         schemaid = oldschema;
3279
3280
3281       /* Now create data blob.  We walk through the (possibly new) schema
3282          and either copy over old data, or insert the new.  */
3283       /* XXX Here we rely on the fact that the (new) schema has the form
3284          o1 o2 o3 o4 ... | n1 n2 n3 ...
3285          (oX being the old keyids (possibly overwritten), and nX being
3286           the new keyids).  This rules out sorting the keyids in order
3287          to ensure a small schema count.  */
3288       if (entry >= 0)
3289         data->incoreoffset[entry] = newincore.len;
3290       data_addid(&newincore, schemaid);
3291       if (entry == -1)
3292         {
3293           data->mainschema = schemaid;
3294           data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
3295         }
3296       /* we don't use a pointer to the schemadata here as repodata_serialize_key
3297        * may call repodata_schema2id() which might realloc our schemadata */
3298       for (schemaidx = data->schemata[schemaid]; (keyid = data->schemadata[schemaidx]) != 0; schemaidx++)
3299         {
3300           if (entry == -1)
3301             data->mainschemaoffsets[schemaidx - data->schemata[schemaid]] = newincore.len;
3302           if (keyid == solvkeyid)
3303             {
3304               /* add flexarray entry count */
3305               data_addid(&newincore, data->end - data->start);
3306               break;
3307             }
3308           key = data->keys + keyid;
3309 #if 0
3310           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));
3311 #endif
3312           ndp = dp;
3313           if (oldcount)
3314             {
3315               /* Skip the data associated with this old key.  */
3316               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3317                 {
3318                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
3319                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
3320                 }
3321               else if (key->storage == KEY_STORAGE_INCORE)
3322                 ndp = data_skip_key(data, dp, key);
3323               oldcount--;
3324             }
3325           if (seen[keyid] == -1)
3326             {
3327               /* If this key was an old one _and_ was not overwritten with
3328                  a different value copy over the old value (we skipped it
3329                  above).  */
3330               if (dp != ndp)
3331                 data_addblob(&newincore, dp, ndp - dp);
3332               seen[keyid] = 0;
3333             }
3334           else if (seen[keyid])
3335             {
3336               /* Otherwise we have a new value.  Parse it into the internal form.  */
3337               repodata_serialize_key(data, &newincore, &newvincore, schema, key, seen[keyid] - 1);
3338             }
3339           dp = ndp;
3340         }
3341       if (entry >= 0 && data->attrs)
3342         {
3343           if (data->attrs[entry])
3344             data->attrs[entry] = solv_free(data->attrs[entry]);
3345           if (entry && entry % 4096 == 0 && data->nxattrs <= 2 && entry + 64 < nentry)
3346             {
3347               compact_attrdata(data, entry + 1, nentry);        /* try to free some memory */
3348 #if 0
3349               printf("  attr data: %d K\n", data->attrdatalen / 1024);
3350               printf("  attrid data: %d K\n", data->attriddatalen / (1024 / 4));
3351               printf("  incore data: %d K\n", newincore.len / 1024);
3352               printf("  sum: %d K\n", (newincore.len + data->attrdatalen + data->attriddatalen * 4) / 1024);
3353               /* malloc_stats(); */
3354 #endif
3355             }
3356         }
3357     }
3358   /* free all xattrs */
3359   for (entry = 0; entry < data->nxattrs; entry++)
3360     if (data->xattrs[entry])
3361       solv_free(data->xattrs[entry]);
3362   data->xattrs = solv_free(data->xattrs);
3363   data->nxattrs = 0;
3364
3365   data->lasthandle = 0;
3366   data->lastkey = 0;
3367   data->lastdatalen = 0;
3368   solv_free(schema);
3369   solv_free(seen);
3370   repodata_free_schemahash(data);
3371
3372   solv_free(data->incoredata);
3373   data->incoredata = newincore.buf;
3374   data->incoredatalen = newincore.len;
3375   data->incoredatafree = 0;
3376
3377   solv_free(data->vincore);
3378   data->vincore = newvincore.buf;
3379   data->vincorelen = newvincore.len;
3380
3381   data->attrs = solv_free(data->attrs);
3382   data->attrdata = solv_free(data->attrdata);
3383   data->attriddata = solv_free(data->attriddata);
3384   data->attrnum64data = solv_free(data->attrnum64data);
3385   data->attrdatalen = 0;
3386   data->attriddatalen = 0;
3387   data->attrnum64datalen = 0;
3388 #if 0
3389   printf("repodata_internalize %d done\n", data->repodataid);
3390   printf("  incore data: %d K\n", data->incoredatalen / 1024);
3391 #endif
3392 }
3393
3394 void
3395 repodata_disable_paging(Repodata *data)
3396 {
3397   if (maybe_load_repodata(data, 0))
3398     {
3399       repopagestore_disable_paging(&data->store);
3400       data->storestate++;
3401     }
3402 }
3403
3404 static void
3405 repodata_load_stub(Repodata *data)
3406 {
3407   Repo *repo = data->repo;
3408   Pool *pool = repo->pool;
3409   int r, i;
3410   struct _Pool_tmpspace oldtmpspace;
3411   Datapos oldpos;
3412
3413   if (!pool->loadcallback)
3414     {
3415       data->state = REPODATA_ERROR;
3416       return;
3417     }
3418   data->state = REPODATA_LOADING;
3419
3420   /* save tmp space and pos */
3421   oldtmpspace = pool->tmpspace;
3422   memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
3423   oldpos = pool->pos;
3424
3425   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
3426
3427   /* restore tmp space and pos */
3428   for (i = 0; i < POOL_TMPSPACEBUF; i++)
3429     solv_free(pool->tmpspace.buf[i]);
3430   pool->tmpspace = oldtmpspace;
3431   if (r && oldpos.repo == repo && oldpos.repodataid == data->repodataid)
3432     memset(&oldpos, 0, sizeof(oldpos));
3433   pool->pos = oldpos;
3434
3435   data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
3436 }
3437
3438 static inline void
3439 repodata_add_stubkey(Repodata *data, Id keyname, Id keytype)
3440 {
3441   Repokey xkey;
3442
3443   xkey.name = keyname;
3444   xkey.type = keytype;
3445   xkey.storage = KEY_STORAGE_INCORE;
3446   xkey.size = 0;
3447   repodata_key2id(data, &xkey, 1);
3448 }
3449
3450 static Repodata *
3451 repodata_add_stub(Repodata **datap)
3452 {
3453   Repodata *data = *datap;
3454   Repo *repo = data->repo;
3455   Id repodataid = data - repo->repodata;
3456   Repodata *sdata = repo_add_repodata(repo, 0);
3457   data = repo->repodata + repodataid;
3458   if (data->end > data->start)
3459     repodata_extend_block(sdata, data->start, data->end - data->start);
3460   sdata->state = REPODATA_STUB;
3461   sdata->loadcallback = repodata_load_stub;
3462   *datap = data;
3463   return sdata;
3464 }
3465
3466 Repodata *
3467 repodata_create_stubs(Repodata *data)
3468 {
3469   Repo *repo = data->repo;
3470   Pool *pool = repo->pool;
3471   Repodata *sdata;
3472   int *stubdataids;
3473   Dataiterator di;
3474   Id xkeyname = 0;
3475   int i, cnt = 0;
3476
3477   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3478   while (dataiterator_step(&di))
3479     if (di.data == data)
3480       cnt++;
3481   dataiterator_free(&di);
3482   if (!cnt)
3483     return data;
3484   stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
3485   for (i = 0; i < cnt; i++)
3486     {
3487       sdata = repodata_add_stub(&data);
3488       stubdataids[i] = sdata - repo->repodata;
3489     }
3490   i = 0;
3491   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3492   sdata = 0;
3493   while (dataiterator_step(&di))
3494     {
3495       if (di.data != data)
3496         continue;
3497       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
3498         {
3499           dataiterator_entersub(&di);
3500           sdata = repo->repodata + stubdataids[i++];
3501           xkeyname = 0;
3502           continue;
3503         }
3504       switch (di.key->type)
3505         {
3506         case REPOKEY_TYPE_ID:
3507           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
3508           break;
3509         case REPOKEY_TYPE_CONSTANTID:
3510           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
3511           break;
3512         case REPOKEY_TYPE_STR:
3513           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
3514           break;
3515         case REPOKEY_TYPE_VOID:
3516           repodata_set_void(sdata, SOLVID_META, di.key->name);
3517           break;
3518         case REPOKEY_TYPE_NUM:
3519           repodata_set_num(sdata, SOLVID_META, di.key->name, SOLV_KV_NUM64(&di.kv));
3520           break;
3521         case_CHKSUM_TYPES:
3522           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
3523           break;
3524         case REPOKEY_TYPE_IDARRAY:
3525           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
3526           if (di.key->name == REPOSITORY_KEYS)
3527             {
3528               if (!xkeyname)
3529                 {
3530                   if (!di.kv.eof)
3531                     xkeyname = di.kv.id;
3532                 }
3533               else
3534                 {
3535                   repodata_add_stubkey(sdata, xkeyname, di.kv.id);
3536                   xkeyname = 0;
3537                 }
3538             }
3539           break;
3540         default:
3541           break;
3542         }
3543     }
3544   dataiterator_free(&di);
3545   for (i = 0; i < cnt; i++)
3546     repodata_internalize(repo->repodata + stubdataids[i]);
3547   solv_free(stubdataids);
3548   return data;
3549 }
3550
3551 unsigned int
3552 repodata_memused(Repodata *data)
3553 {
3554   return data->incoredatalen + data->vincorelen;
3555 }
3556