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