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