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