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