help gcc inliner a bit
[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; len++)
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)
478     return 0;
479   if (off >= data->lastverticaloffset)
480     {
481       off -= data->lastverticaloffset;
482       if (off + len > data->vincorelen)
483         return 0;
484       return data->vincore + off;
485     }
486   if (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   if (dp)
493     dp += off % REPOPAGE_BLOBSIZE;
494   return dp;
495 }
496
497 static inline unsigned char *
498 get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
499 {
500   unsigned char *dp = *dpp;
501
502   if (!dp)
503     return 0;
504   if (key->storage == KEY_STORAGE_INCORE)
505     {
506       if (advance)
507         *dpp = data_skip_key(data, dp, key);
508       return dp;
509     }
510   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
511     {
512       Id off, len;
513       dp = data_read_id(dp, &off);
514       dp = data_read_id(dp, &len);
515       if (advance)
516         *dpp = dp;
517       return get_vertical_data(data, key, off, len);
518     }
519   return 0;
520 }
521
522 static int
523 load_repodata(Repodata *data)
524 {
525   if (data->loadcallback)
526     {
527       data->loadcallback(data);
528       if (data->state == REPODATA_AVAILABLE)
529         return 1;
530     }
531   data->state = REPODATA_ERROR;
532   return 0;
533 }
534
535 static inline int
536 maybe_load_repodata(Repodata *data, Id keyname)
537 {
538   if (keyname && !repodata_precheck_keyname(data, keyname))
539     return 0;   /* do not bother... */
540   switch(data->state)
541     {
542     case REPODATA_STUB:
543       if (keyname)
544         {
545           int i;
546           for (i = 1; i < data->nkeys; i++)
547             if (keyname == data->keys[i].name)
548               break;
549           if (i == data->nkeys)
550             return 0;
551         }
552       return load_repodata(data);
553     case REPODATA_ERROR:
554       return 0;
555     case REPODATA_AVAILABLE:
556     case REPODATA_LOADING:
557       return 1;
558     default:
559       data->state = REPODATA_ERROR;
560       return 0;
561     }
562 }
563
564 static inline unsigned char *
565 solvid2data(Repodata *data, Id solvid, Id *schemap)
566 {
567   unsigned char *dp = data->incoredata;
568   if (!dp)
569     return 0;
570   if (solvid == SOLVID_META)    /* META */
571     dp += 1;
572   else if (solvid == SOLVID_POS)        /* META */
573     {
574       Pool *pool = data->repo->pool;
575       if (data->repo != pool->pos.repo)
576         return 0;
577       if (data != data->repo->repodata + pool->pos.repodataid)
578         return 0;
579       *schemap = pool->pos.schema;
580       return data->incoredata + pool->pos.dp;
581     }
582   else
583     {
584       if (solvid < data->start || solvid >= data->end)
585         return 0;
586       dp += data->incoreoffset[solvid - data->start];
587     }
588   return data_read_id(dp, schemap);
589 }
590
591 /************************************************************************
592  * data lookup
593  */
594
595 static inline unsigned char *
596 find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
597 {
598   unsigned char *dp;
599   Id schema, *keyp, *kp;
600   Repokey *key;
601
602   if (!maybe_load_repodata(data, keyname))
603     return 0;
604   dp = solvid2data(data, solvid, &schema);
605   if (!dp)
606     return 0;
607   keyp = data->schemadata + data->schemata[schema];
608   for (kp = keyp; *kp; kp++)
609     if (data->keys[*kp].name == keyname)
610       break;
611   if (!*kp)
612     return 0;
613   *keypp = key = data->keys + *kp;
614   if (key->type == REPOKEY_TYPE_DELETED)
615     return 0;
616   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
617     return dp;  /* no need to forward... */
618   dp = forward_to_key(data, *kp, keyp, dp);
619   if (!dp)
620     return 0;
621   return get_data(data, key, &dp, 0);
622 }
623
624 Id
625 repodata_lookup_type(Repodata *data, Id solvid, Id keyname)
626 {
627   Id schema, *keyp, *kp;
628   if (!maybe_load_repodata(data, keyname))
629     return 0;
630   if (!solvid2data(data, solvid, &schema))
631     return 0;
632   keyp = data->schemadata + data->schemata[schema];
633   for (kp = keyp; *kp; kp++)
634     if (data->keys[*kp].name == keyname)
635       return data->keys[*kp].type;
636   return 0;
637 }
638
639 Id
640 repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
641 {
642   unsigned char *dp;
643   Repokey *key;
644   Id id;
645
646   dp = find_key_data(data, solvid, keyname, &key);
647   if (!dp)
648     return 0;
649   if (key->type == REPOKEY_TYPE_CONSTANTID)
650     return key->size;
651   if (key->type != REPOKEY_TYPE_ID)
652     return 0;
653   dp = data_read_id(dp, &id);
654   return id;
655 }
656
657 const char *
658 repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
659 {
660   unsigned char *dp;
661   Repokey *key;
662   Id id;
663
664   dp = find_key_data(data, solvid, keyname, &key);
665   if (!dp)
666     return 0;
667   if (key->type == REPOKEY_TYPE_STR)
668     return (const char *)dp;
669   if (key->type == REPOKEY_TYPE_CONSTANTID)
670     id = key->size;
671   else if (key->type == REPOKEY_TYPE_ID)
672     dp = data_read_id(dp, &id);
673   else
674     return 0;
675   if (data->localpool)
676     return stringpool_id2str(&data->spool, id);
677   return pool_id2str(data->repo->pool, id);
678 }
679
680 int
681 repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long *value)
682 {
683   unsigned char *dp;
684   Repokey *key;
685   unsigned int high, low;
686
687   *value = 0;
688   dp = find_key_data(data, solvid, keyname, &key);
689   if (!dp)
690     return 0;
691   switch (key->type)
692     {
693     case REPOKEY_TYPE_NUM:
694       data_read_num64(dp, &low, &high);
695       *value = (unsigned long long)high << 32 | low;
696       return 1;
697     case REPOKEY_TYPE_U32:
698       data_read_u32(dp, &low);
699       *value = low;
700       return 1;
701     case REPOKEY_TYPE_CONSTANT:
702       *value = key->size;
703       return 1;
704     default:
705       return 0;
706     }
707 }
708
709 int
710 repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
711 {
712   Id schema;
713   Id *keyp;
714   unsigned char *dp;
715
716   if (!maybe_load_repodata(data, keyname))
717     return 0;
718   dp = solvid2data(data, solvid, &schema);
719   if (!dp)
720     return 0;
721   /* can't use find_key_data as we need to test the type */
722   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
723     if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
724       return 1;
725   return 0;
726 }
727
728 const unsigned char *
729 repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
730 {
731   unsigned char *dp;
732   Repokey *key;
733
734   dp = find_key_data(data, solvid, keyname, &key);
735   if (!dp)
736     return 0;
737   if (!(key->type == REPOKEY_TYPE_MD5 || key->type == REPOKEY_TYPE_SHA1 || key->type == REPOKEY_TYPE_SHA256))
738     return 0;
739   *typep = key->type;
740   return dp;
741 }
742
743 int
744 repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
745 {
746   unsigned char *dp;
747   Repokey *key;
748   Id id;
749   int eof = 0;
750
751   queue_empty(q);
752   dp = find_key_data(data, solvid, keyname, &key);
753   if (!dp)
754     return 0;
755   if (key->type != REPOKEY_TYPE_IDARRAY && key->type != REPOKEY_TYPE_REL_IDARRAY)
756     return 0;
757   for (;;)
758     {
759       dp = data_read_ideof(dp, &id, &eof);
760       queue_push(q, id);
761       if (eof)
762         break;
763     }
764   return 1;
765 }
766
767 Id
768 repodata_globalize_id(Repodata *data, Id id, int create)
769 {
770   if (!id || !data || !data->localpool)
771     return id;
772   return pool_str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
773 }
774
775 Id
776 repodata_localize_id(Repodata *data, Id id, int create)
777 {
778   if (!id || !data || !data->localpool)
779     return id;
780   return stringpool_str2id(&data->spool, pool_id2str(data->repo->pool, id), create);
781 }
782
783 Id
784 repodata_lookup_id_uninternalized(Repodata *data, Id solvid, Id keyname, Id voidid)
785 {
786   Id *ap;
787   if (!data->attrs)
788     return 0;
789   ap = data->attrs[solvid - data->start];
790   if (!ap)
791     return 0;
792   for (; *ap; ap += 2)
793     {
794       if (data->keys[*ap].name != keyname)
795         continue;
796       if (data->keys[*ap].type == REPOKEY_TYPE_VOID)
797         return voidid;
798       if (data->keys[*ap].type == REPOKEY_TYPE_ID)
799         return ap[1];
800       return 0;
801     }
802   return 0;
803 }
804
805
806 /************************************************************************
807  * data search
808  */
809
810
811 int
812 repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
813 {
814   switch (key->type)
815     {
816     case REPOKEY_TYPE_ID:
817     case REPOKEY_TYPE_CONSTANTID:
818     case REPOKEY_TYPE_IDARRAY:
819       if (data && data->localpool)
820         kv->str = stringpool_id2str(&data->spool, kv->id);
821       else
822         kv->str = pool_id2str(pool, kv->id);
823       if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
824         {
825           const char *s;
826           for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
827             ;
828           if (*s == ':' && s > kv->str)
829             kv->str = s + 1;
830         }
831       return 1;
832     case REPOKEY_TYPE_STR:
833       return 1;
834     case REPOKEY_TYPE_DIRSTRARRAY:
835       if (!(flags & SEARCH_FILES))
836         return 1;       /* match just the basename */
837       /* Put the full filename into kv->str.  */
838       kv->str = repodata_dir2str(data, kv->id, kv->str);
839       /* And to compensate for that put the "empty" directory into
840          kv->id, so that later calls to repodata_dir2str on this data
841          come up with the same filename again.  */
842       kv->id = 0;
843       return 1;
844     case REPOKEY_TYPE_MD5:
845     case REPOKEY_TYPE_SHA1:
846     case REPOKEY_TYPE_SHA256:
847       if (!(flags & SEARCH_CHECKSUMS))
848         return 0;       /* skip em */
849       kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
850       return 1;
851     default:
852       return 0;
853     }
854 }
855
856
857 struct subschema_data {
858   Solvable *s;
859   void *cbdata;
860   KeyValue *parent;
861 };
862
863 /* search a specific repodata */
864 void
865 repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
866 {
867   Id schema;
868   Repokey *key;
869   Id keyid, *kp, *keyp;
870   unsigned char *dp, *ddp;
871   int onekey = 0;
872   int stop;
873   KeyValue kv;
874   Solvable *s;
875
876   if (!maybe_load_repodata(data, keyname))
877     return;
878   if (solvid == SOLVID_SUBSCHEMA)
879     {
880       struct subschema_data *subd = cbdata;
881       cbdata = subd->cbdata;
882       s = subd->s;
883       schema = subd->parent->id;
884       dp = (unsigned char *)subd->parent->str;
885       kv.parent = subd->parent;
886     }
887   else
888     {
889       schema = 0;
890       dp = solvid2data(data, solvid, &schema);
891       if (!dp)
892         return;
893       s = data->repo->pool->solvables + solvid;
894       kv.parent = 0;
895     }
896   keyp = data->schemadata + data->schemata[schema];
897   if (keyname)
898     {
899       /* search for a specific key */
900       for (kp = keyp; *kp; kp++)
901         if (data->keys[*kp].name == keyname)
902           break;
903       if (!*kp)
904         return;
905       dp = forward_to_key(data, *kp, keyp, dp);
906       if (!dp)
907         return;
908       keyp = kp;
909       onekey = 1;
910     }
911   while ((keyid = *keyp++) != 0)
912     {
913       stop = 0;
914       key = data->keys + keyid;
915       ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
916
917       if (key->type == REPOKEY_TYPE_DELETED)
918         continue;
919       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
920         {
921           struct subschema_data subd;
922           int nentries;
923           Id schema = 0;
924
925           subd.cbdata = cbdata;
926           subd.s = s;
927           subd.parent = &kv;
928           ddp = data_read_id(ddp, &nentries);
929           kv.num = nentries;
930           kv.entry = 0;
931           kv.eof = 0;
932           while (ddp && nentries > 0)
933             {
934               if (!--nentries)
935                 kv.eof = 1;
936               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
937                 ddp = data_read_id(ddp, &schema);
938               kv.id = schema;
939               kv.str = (char *)ddp;
940               stop = callback(cbdata, s, data, key, &kv);
941               if (stop > SEARCH_NEXT_KEY)
942                 return;
943               if (stop && stop != SEARCH_ENTERSUB)
944                 break;
945               if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
946                 repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
947               ddp = data_skip_schema(data, ddp, schema);
948               kv.entry++;
949             }
950           if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
951             {
952               /* sentinel */
953               kv.eof = 2;
954               kv.str = (char *)ddp;
955               stop = callback(cbdata, s, data, key, &kv);
956               if (stop > SEARCH_NEXT_KEY)
957                 return;
958             }
959           if (onekey)
960             return;
961           continue;
962         }
963       kv.entry = 0;
964       do
965         {
966           ddp = data_fetch(ddp, &kv, key);
967           if (!ddp)
968             break;
969           stop = callback(cbdata, s, data, key, &kv);
970           kv.entry++;
971         }
972       while (!kv.eof && !stop);
973       if (onekey || stop > SEARCH_NEXT_KEY)
974         return;
975     }
976 }
977
978 void
979 repodata_setpos_kv(Repodata *data, KeyValue *kv)
980 {
981   Pool *pool = data->repo->pool;
982   if (!kv)
983     pool_clear_pos(pool);
984   else
985     {
986       pool->pos.repo = data->repo;
987       pool->pos.repodataid = data - data->repo->repodata;
988       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
989       pool->pos.schema = kv->id;
990     }
991 }
992
993 /************************************************************************
994  * data iterator functions
995  */
996
997 static inline Id *
998 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
999 {
1000   kv->id = keyname;
1001   switch (keyname)
1002     {
1003     case SOLVABLE_NAME:
1004       kv->eof = 1;
1005       return &s->name;
1006     case SOLVABLE_ARCH:
1007       kv->eof = 1;
1008       return &s->arch;
1009     case SOLVABLE_EVR:
1010       kv->eof = 1;
1011       return &s->evr;
1012     case SOLVABLE_VENDOR:
1013       kv->eof = 1;
1014       return &s->vendor;
1015     case SOLVABLE_PROVIDES:
1016       kv->eof = 0;
1017       return s->provides ? s->repo->idarraydata + s->provides : 0;
1018     case SOLVABLE_OBSOLETES:
1019       kv->eof = 0;
1020       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
1021     case SOLVABLE_CONFLICTS:
1022       kv->eof = 0;
1023       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
1024     case SOLVABLE_REQUIRES:
1025       kv->eof = 0;
1026       return s->requires ? s->repo->idarraydata + s->requires : 0;
1027     case SOLVABLE_RECOMMENDS:
1028       kv->eof = 0;
1029       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
1030     case SOLVABLE_SUPPLEMENTS:
1031       kv->eof = 0;
1032       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
1033     case SOLVABLE_SUGGESTS:
1034       kv->eof = 0;
1035       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
1036     case SOLVABLE_ENHANCES:
1037       kv->eof = 0;
1038       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
1039     case RPM_RPMDBID:
1040       kv->eof = 1;
1041       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
1042     default:
1043       return 0;
1044     }
1045 }
1046
1047 int
1048 datamatcher_init(Datamatcher *ma, const char *match, int flags)
1049 {
1050   ma->match = match;
1051   ma->flags = flags;
1052   ma->error = 0;
1053   ma->matchdata = 0;
1054   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1055     {
1056       ma->matchdata = solv_calloc(1, sizeof(regex_t));
1057       ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
1058       if (ma->error)
1059         {
1060           solv_free(ma->matchdata);
1061           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
1062         }
1063     }
1064   return ma->error;
1065 }
1066
1067 void
1068 datamatcher_free(Datamatcher *ma)
1069 {
1070   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
1071     {
1072       regfree(ma->matchdata);
1073       ma->matchdata = solv_free(ma->matchdata);
1074     }
1075 }
1076
1077 int
1078 datamatcher_match(Datamatcher *ma, const char *str)
1079 {
1080   int l;
1081   switch ((ma->flags & SEARCH_STRINGMASK))
1082     {
1083     case SEARCH_SUBSTRING:
1084       if (ma->flags & SEARCH_NOCASE)
1085         {
1086           if (!strcasestr(str, ma->match))
1087             return 0;
1088         }
1089       else
1090         {
1091           if (!strstr(str, ma->match))
1092             return 0;
1093         }
1094       break;
1095     case SEARCH_STRING:
1096       if (ma->flags & SEARCH_NOCASE)
1097         {
1098           if (strcasecmp(ma->match, str))
1099             return 0;
1100         }
1101       else
1102         {
1103           if (strcmp(ma->match, str))
1104             return 0;
1105         }
1106       break;
1107     case SEARCH_STRINGSTART:
1108       if (ma->flags & SEARCH_NOCASE)
1109         {
1110           if (strncasecmp(ma->match, str, strlen(ma->match)))
1111             return 0;
1112         }
1113       else
1114         {
1115           if (strncmp(ma->match, str, strlen(ma->match)))
1116             return 0;
1117         }
1118       break;
1119     case SEARCH_STRINGEND:
1120       l = strlen(str) - strlen(ma->match);
1121       if (l < 0)
1122         return 0;
1123       if (ma->flags & SEARCH_NOCASE)
1124         {
1125           if (strcasecmp(ma->match, str + l))
1126             return 0;
1127         }
1128       else
1129         {
1130           if (strcmp(ma->match, str + l))
1131             return 0;
1132         }
1133       break;
1134     case SEARCH_GLOB:
1135       if (fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0))
1136         return 0;
1137       break;
1138     case SEARCH_REGEX:
1139       if (regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0))
1140         return 0;
1141       break;
1142     default:
1143       return 0;
1144     }
1145   return 1;
1146 }
1147
1148 int
1149 repodata_filelistfilter_matches(Repodata *data, const char *str)
1150 {
1151   /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
1152   /* for now hardcoded */
1153   if (strstr(str, "bin/"))
1154     return 1;
1155   if (!strncmp(str, "/etc/", 5))
1156     return 1;
1157   if (!strcmp(str, "/usr/lib/sendmail"))
1158     return 1;
1159   return 0;
1160 }
1161
1162
1163 enum {
1164   di_bye,
1165
1166   di_enterrepo,
1167   di_entersolvable,
1168   di_enterrepodata,
1169   di_enterschema,
1170   di_enterkey,
1171
1172   di_nextattr,
1173   di_nextkey,
1174   di_nextrepodata,
1175   di_nextsolvable,
1176   di_nextrepo,
1177
1178   di_enterarray,
1179   di_nextarrayelement,
1180
1181   di_entersub,
1182   di_leavesub,
1183
1184   di_nextsolvablekey,
1185   di_entersolvablekey,
1186   di_nextsolvableattr
1187 };
1188
1189 /* see dataiterator.h for documentation */
1190 int
1191 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
1192 {
1193   memset(di, 0, sizeof(*di));
1194   di->pool = pool;
1195   di->flags = flags & ~SEARCH_THISSOLVID;
1196   if (!pool || (repo && repo->pool != pool))
1197     {
1198       di->state = di_bye;
1199       return -1;
1200     }
1201   if (match)
1202     {
1203       int error;
1204       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1205         {
1206           di->state = di_bye;
1207           return error;
1208         }
1209     }
1210   di->keyname = keyname;
1211   di->keynames[0] = keyname;
1212   dataiterator_set_search(di, repo, p);
1213   return 0;
1214 }
1215
1216 void
1217 dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
1218 {
1219   *di = *from;
1220   memset(&di->matcher, 0, sizeof(di->matcher));
1221   if (from->matcher.match)
1222     datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
1223   if (di->nparents)
1224     {
1225       /* fix pointers */
1226       int i;
1227       for (i = 1; i < di->nparents; i++)
1228         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1229       di->kv.parent = &di->parents[di->nparents - 1].kv;
1230     }
1231 }
1232
1233 int
1234 dataiterator_set_match(Dataiterator *di, const char *match, int flags)
1235 {
1236   di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
1237   datamatcher_free(&di->matcher);
1238   memset(&di->matcher, 0, sizeof(di->matcher));
1239   if (match)
1240     {
1241       int error;
1242       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1243         {
1244           di->state = di_bye;
1245           return error;
1246         }
1247     }
1248   return 0;
1249 }
1250
1251 void
1252 dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
1253 {
1254   di->repo = repo;
1255   di->repoid = 0;
1256   di->flags &= ~SEARCH_THISSOLVID;
1257   di->nparents = 0;
1258   di->rootlevel = 0;
1259   di->repodataid = 1;
1260   if (!di->pool->urepos)
1261     {
1262       di->state = di_bye;
1263       return;
1264     }
1265   if (!repo)
1266     {
1267       di->repoid = 1;
1268       di->repo = di->pool->repos[di->repoid];
1269     }
1270   di->state = di_enterrepo;
1271   if (p)
1272     dataiterator_jump_to_solvid(di, p);
1273 }
1274
1275 void
1276 dataiterator_set_keyname(Dataiterator *di, Id keyname)
1277 {
1278   di->nkeynames = 0;
1279   di->keyname = keyname;
1280   di->keynames[0] = keyname;
1281 }
1282
1283 void
1284 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
1285 {
1286   int i;
1287
1288   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
1289     {
1290       di->state = di_bye;       /* sorry */
1291       return;
1292     }
1293   for (i = di->nkeynames + 1; i > 0; i--)
1294     di->keynames[i] = di->keynames[i - 1];
1295   di->keynames[0] = di->keyname = keyname;
1296   di->nkeynames++;
1297 }
1298
1299 void
1300 dataiterator_free(Dataiterator *di)
1301 {
1302   if (di->matcher.match)
1303     datamatcher_free(&di->matcher);
1304 }
1305
1306 static inline unsigned char *
1307 dataiterator_find_keyname(Dataiterator *di, Id keyname)
1308 {
1309   Id *keyp = di->keyp;
1310   Repokey *keys = di->data->keys;
1311   unsigned char *dp;
1312
1313   for (keyp = di->keyp; *keyp; keyp++)
1314     if (keys[*keyp].name == keyname)
1315       break;
1316   if (!*keyp)
1317     return 0;
1318   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1319   if (!dp)
1320     return 0;
1321   di->keyp = keyp;
1322   return dp;
1323 }
1324
1325 static int
1326 dataiterator_filelistcheck(Dataiterator *di)
1327 {
1328   int j;
1329   int needcomplete = 0;
1330   Repodata *data = di->data;
1331
1332   if ((di->matcher.flags & SEARCH_COMPLETE_FILELIST) != 0)
1333     if (!di->matcher.match
1334        || ((di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
1335            && (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB)
1336        || !repodata_filelistfilter_matches(di->data, di->matcher.match))
1337       needcomplete = 1;
1338   if (data->state != REPODATA_AVAILABLE)
1339     return needcomplete ? 1 : 0;
1340   for (j = 1; j < data->nkeys; j++)
1341     if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1342       break;
1343   return j == data->nkeys && !needcomplete ? 0 : 1;
1344 }
1345
1346 int
1347 dataiterator_step(Dataiterator *di)
1348 {
1349   Id schema;
1350
1351   for (;;)
1352     {
1353       switch (di->state)
1354         {
1355         case di_enterrepo: di_enterrepo:
1356           if (!di->repo || (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS)))
1357             goto di_nextrepo;
1358           if (!(di->flags & SEARCH_THISSOLVID))
1359             {
1360               di->solvid = di->repo->start - 1; /* reset solvid iterator */
1361               goto di_nextsolvable;
1362             }
1363           /* FALLTHROUGH */
1364
1365         case di_entersolvable: di_entersolvable:
1366           if (di->repodataid)
1367             {
1368               di->repodataid = 1;       /* reset repodata iterator */
1369               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)
1370                 {
1371                   extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
1372
1373                   di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1374                   di->data = 0;
1375                   goto di_entersolvablekey;
1376                 }
1377             }
1378           /* FALLTHROUGH */
1379
1380         case di_enterrepodata: di_enterrepodata:
1381           if (di->repodataid)
1382             {
1383               if (di->repodataid >= di->repo->nrepodata)
1384                 goto di_nextsolvable;
1385               di->data = di->repo->repodata + di->repodataid;
1386             }
1387           if (di->repodataid && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
1388             goto di_nextrepodata;
1389           if (!maybe_load_repodata(di->data, di->keyname))
1390             goto di_nextrepodata;
1391           di->dp = solvid2data(di->data, di->solvid, &schema);
1392           if (!di->dp)
1393             goto di_nextrepodata;
1394           if (di->solvid == SOLVID_POS)
1395             di->solvid = di->pool->pos.solvid;
1396           /* reset key iterator */
1397           di->keyp = di->data->schemadata + di->data->schemata[schema];
1398           /* FALLTHROUGH */
1399
1400         case di_enterschema: di_enterschema:
1401           if (di->keyname)
1402             di->dp = dataiterator_find_keyname(di, di->keyname);
1403           if (!di->dp || !*di->keyp)
1404             {
1405               if (di->kv.parent)
1406                 goto di_leavesub;
1407               goto di_nextrepodata;
1408             }
1409           /* FALLTHROUGH */
1410
1411         case di_enterkey: di_enterkey:
1412           di->kv.entry = -1;
1413           di->key = di->data->keys + *di->keyp;
1414           di->ddp = get_data(di->data, di->key, &di->dp, di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0) ? 1 : 0);
1415           if (!di->ddp)
1416             goto di_nextkey;
1417           if (di->key->type == REPOKEY_TYPE_DELETED)
1418             goto di_nextkey;
1419           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1420             goto di_enterarray;
1421           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1422             goto di_nextkey;
1423           /* FALLTHROUGH */
1424
1425         case di_nextattr:
1426           di->kv.entry++;
1427           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1428           if (di->kv.eof)
1429             di->state = di_nextkey;
1430           else
1431             di->state = di_nextattr;
1432           break;
1433
1434         case di_nextkey: di_nextkey:
1435           if (!di->keyname && *++di->keyp)
1436             goto di_enterkey;
1437           if (di->kv.parent)
1438             goto di_leavesub;
1439           /* FALLTHROUGH */
1440
1441         case di_nextrepodata: di_nextrepodata:
1442           if (di->repodataid && ++di->repodataid < di->repo->nrepodata)
1443               goto di_enterrepodata;
1444           /* FALLTHROUGH */
1445
1446         case di_nextsolvable: di_nextsolvable:
1447           if (!(di->flags & SEARCH_THISSOLVID))
1448             {
1449               if (di->solvid < 0)
1450                 di->solvid = di->repo->start;
1451               else
1452                 di->solvid++;
1453               for (; di->solvid < di->repo->end; di->solvid++)
1454                 {
1455                   if (di->pool->solvables[di->solvid].repo == di->repo)
1456                     goto di_entersolvable;
1457                 }
1458             }
1459           /* FALLTHROUGH */
1460
1461         case di_nextrepo: di_nextrepo:
1462           if (di->repoid > 0)
1463             {
1464               di->repoid++;
1465               di->repodataid = 1;
1466               if (di->repoid < di->pool->nrepos)
1467                 {
1468                   di->repo = di->pool->repos[di->repoid];
1469                   goto di_enterrepo;
1470                 }
1471             }
1472         /* FALLTHROUGH */
1473
1474         case di_bye: di_bye:
1475           di->state = di_bye;
1476           return 0;
1477
1478         case di_enterarray: di_enterarray:
1479           if (di->key->name == REPOSITORY_SOLVABLES)
1480             goto di_nextkey;
1481           di->ddp = data_read_id(di->ddp, (Id *)&di->kv.num);
1482           di->kv.eof = 0;
1483           di->kv.entry = -1;
1484           /* FALLTHROUGH */
1485
1486         case di_nextarrayelement: di_nextarrayelement:
1487           di->kv.entry++;
1488           if (di->kv.entry)
1489             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1490           if (di->kv.entry == di->kv.num)
1491             {
1492               if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1493                 goto di_nextkey;
1494               if (!(di->flags & SEARCH_ARRAYSENTINEL))
1495                 goto di_nextkey;
1496               di->kv.str = (char *)di->ddp;
1497               di->kv.eof = 2;
1498               di->state = di_nextkey;
1499               break;
1500             }
1501           if (di->kv.entry == di->kv.num - 1)
1502             di->kv.eof = 1;
1503           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1504             di->ddp = data_read_id(di->ddp, &di->kv.id);
1505           di->kv.str = (char *)di->ddp;
1506           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1507             goto di_entersub;
1508           if ((di->flags & SEARCH_SUB) != 0)
1509             di->state = di_entersub;
1510           else
1511             di->state = di_nextarrayelement;
1512           break;
1513
1514         case di_entersub: di_entersub:
1515           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1516             goto di_nextarrayelement;   /* sorry, full */
1517           di->parents[di->nparents].kv = di->kv;
1518           di->parents[di->nparents].dp = di->dp;
1519           di->parents[di->nparents].keyp = di->keyp;
1520           di->dp = (unsigned char *)di->kv.str;
1521           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1522           memset(&di->kv, 0, sizeof(di->kv));
1523           di->kv.parent = &di->parents[di->nparents].kv;
1524           di->nparents++;
1525           di->keyname = di->keynames[di->nparents - di->rootlevel];
1526           goto di_enterschema;
1527
1528         case di_leavesub: di_leavesub:
1529           if (di->nparents - 1 < di->rootlevel)
1530             goto di_bye;
1531           di->nparents--;
1532           di->dp = di->parents[di->nparents].dp;
1533           di->kv = di->parents[di->nparents].kv;
1534           di->keyp = di->parents[di->nparents].keyp;
1535           di->key = di->data->keys + *di->keyp;
1536           di->ddp = (unsigned char *)di->kv.str;
1537           di->keyname = di->keynames[di->nparents - di->rootlevel];
1538           goto di_nextarrayelement;
1539
1540         /* special solvable attr handling follows */
1541
1542         case di_nextsolvablekey: di_nextsolvablekey:
1543           if (di->keyname || di->key->name == RPM_RPMDBID)
1544             goto di_enterrepodata;
1545           di->key++;
1546           /* FALLTHROUGH */
1547
1548         case di_entersolvablekey: di_entersolvablekey:
1549           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1550           if (!di->idp || !*di->idp)
1551             goto di_nextsolvablekey;
1552           if (di->kv.eof)
1553             {
1554               /* not an array */
1555               di->kv.id = *di->idp;
1556               di->kv.num = *di->idp;    /* for rpmdbid */
1557               di->kv.num2 = 0;          /* for rpmdbid */
1558               di->kv.entry = 0;
1559               di->state = di_nextsolvablekey;
1560               break;
1561             }
1562           di->kv.entry = -1;
1563           /* FALLTHROUGH */
1564
1565         case di_nextsolvableattr:
1566           di->state = di_nextsolvableattr;
1567           di->kv.id = *di->idp++;
1568           di->kv.entry++;
1569           if (!*di->idp)
1570             {
1571               di->kv.eof = 1;
1572               di->state = di_nextsolvablekey;
1573             }
1574           break;
1575
1576         }
1577
1578       if (di->matcher.match)
1579         {
1580           /* simple pre-check so that we don't need to stringify */
1581           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && di->matcher.match && (di->matcher.flags & (SEARCH_FILES|SEARCH_NOCASE|SEARCH_STRINGMASK)) == (SEARCH_FILES|SEARCH_STRING))
1582             {
1583               int l = strlen(di->matcher.match) - strlen(di->kv.str);
1584               if (l < 0 || strcmp(di->matcher.match + l, di->kv.str))
1585                 continue;
1586             }
1587           if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1588             {
1589               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1590                 return 1;
1591               continue;
1592             }
1593           if (!datamatcher_match(&di->matcher, di->kv.str))
1594             continue;
1595         }
1596       /* found something! */
1597       return 1;
1598     }
1599 }
1600
1601 void
1602 dataiterator_entersub(Dataiterator *di)
1603 {
1604   if (di->state == di_nextarrayelement)
1605     di->state = di_entersub;
1606 }
1607
1608 void
1609 dataiterator_setpos(Dataiterator *di)
1610 {
1611   if (di->kv.eof == 2)
1612     {
1613       pool_clear_pos(di->pool);
1614       return;
1615     }
1616   di->pool->pos.solvid = di->solvid;
1617   di->pool->pos.repo = di->repo;
1618   di->pool->pos.repodataid = di->data - di->repo->repodata;
1619   di->pool->pos.schema = di->kv.id;
1620   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1621 }
1622
1623 void
1624 dataiterator_setpos_parent(Dataiterator *di)
1625 {
1626   if (!di->kv.parent || di->kv.parent->eof == 2)
1627     {
1628       pool_clear_pos(di->pool);
1629       return;
1630     }
1631   di->pool->pos.solvid = di->solvid;
1632   di->pool->pos.repo = di->repo;
1633   di->pool->pos.repodataid = di->data - di->repo->repodata;
1634   di->pool->pos.schema = di->kv.parent->id;
1635   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1636 }
1637
1638 /* clones just the position, not the search keys/matcher */
1639 void
1640 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
1641 {
1642   di->state = from->state;
1643   di->flags &= ~SEARCH_THISSOLVID;
1644   di->flags |= (from->flags & SEARCH_THISSOLVID);
1645   di->repo = from->repo;
1646   di->data = from->data;
1647   di->dp = from->dp;
1648   di->ddp = from->ddp;
1649   di->idp = from->idp;
1650   di->keyp = from->keyp;
1651   di->key = from->key;
1652   di->kv = from->kv;
1653   di->repodataid = from->repodataid;
1654   di->solvid = from->solvid;
1655   di->repoid = from->repoid;
1656   di->rootlevel = from->rootlevel;
1657   memcpy(di->parents, from->parents, sizeof(from->parents));
1658   di->nparents = from->nparents;
1659   if (di->nparents)
1660     {
1661       int i;
1662       for (i = 1; i < di->nparents; i++)
1663         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1664       di->kv.parent = &di->parents[di->nparents - 1].kv;
1665     }
1666 }
1667
1668 void
1669 dataiterator_seek(Dataiterator *di, int whence)
1670 {
1671   if ((whence & DI_SEEK_STAY) != 0)
1672     di->rootlevel = di->nparents;
1673   switch (whence & ~DI_SEEK_STAY)
1674     {
1675     case DI_SEEK_CHILD:
1676       if (di->state != di_nextarrayelement)
1677         break;
1678       if ((whence & DI_SEEK_STAY) != 0)
1679         di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
1680       di->state = di_entersub;
1681       break;
1682     case DI_SEEK_PARENT:
1683       if (!di->nparents)
1684         {
1685           di->state = di_bye;
1686           break;
1687         }
1688       di->nparents--;
1689       if (di->rootlevel > di->nparents)
1690         di->rootlevel = di->nparents;
1691       di->dp = di->parents[di->nparents].dp;
1692       di->kv = di->parents[di->nparents].kv;
1693       di->keyp = di->parents[di->nparents].keyp;
1694       di->key = di->data->keys + *di->keyp;
1695       di->ddp = (unsigned char *)di->kv.str;
1696       di->keyname = di->keynames[di->nparents - di->rootlevel];
1697       di->state = di_nextarrayelement;
1698       break;
1699     case DI_SEEK_REWIND:
1700       if (!di->nparents)
1701         {
1702           di->state = di_bye;
1703           break;
1704         }
1705       di->dp = (unsigned char *)di->kv.parent->str;
1706       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
1707       di->state = di_enterschema;
1708       break;
1709     default:
1710       break;
1711     }
1712 }
1713
1714 void
1715 dataiterator_skip_attribute(Dataiterator *di)
1716 {
1717   if (di->state == di_nextsolvableattr)
1718     di->state = di_nextsolvablekey;
1719   else
1720     di->state = di_nextkey;
1721 }
1722
1723 void
1724 dataiterator_skip_solvable(Dataiterator *di)
1725 {
1726   di->nparents = 0;
1727   di->kv.parent = 0;
1728   di->rootlevel = 0;
1729   di->keyname = di->keynames[0];
1730   di->state = di_nextsolvable;
1731 }
1732
1733 void
1734 dataiterator_skip_repo(Dataiterator *di)
1735 {
1736   di->nparents = 0;
1737   di->kv.parent = 0;
1738   di->rootlevel = 0;
1739   di->keyname = di->keynames[0];
1740   di->state = di_nextrepo;
1741 }
1742
1743 void
1744 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
1745 {
1746   di->nparents = 0;
1747   di->kv.parent = 0;
1748   di->rootlevel = 0;
1749   di->keyname = di->keynames[0];
1750   if (solvid == SOLVID_POS)
1751     {
1752       di->repo = di->pool->pos.repo;
1753       if (!di->repo)
1754         {
1755           di->state = di_bye;
1756           return;
1757         }
1758       di->repoid = 0;
1759       di->data = di->repo->repodata + di->pool->pos.repodataid;
1760       di->repodataid = 0;
1761       di->solvid = solvid;
1762       di->state = di_enterrepo;
1763       di->flags |= SEARCH_THISSOLVID;
1764       return;
1765     }
1766   if (solvid > 0)
1767     {
1768       di->repo = di->pool->solvables[solvid].repo;
1769       di->repoid = 0;
1770     }
1771   else if (di->repoid > 0)
1772     {
1773       if (!di->pool->urepos)
1774         {
1775           di->state = di_bye;
1776           return;
1777         }
1778       di->repoid = 1;
1779       di->repo = di->pool->repos[di->repoid];
1780     }
1781   di->repodataid = 1;
1782   di->solvid = solvid;
1783   if (solvid)
1784     di->flags |= SEARCH_THISSOLVID;
1785   di->state = di_enterrepo;
1786 }
1787
1788 void
1789 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1790 {
1791   di->nparents = 0;
1792   di->kv.parent = 0;
1793   di->rootlevel = 0;
1794   di->repo = repo;
1795   di->repoid = 0;       /* 0 means stay at repo */
1796   di->repodataid = 1;
1797   di->solvid = 0;
1798   di->flags &= ~SEARCH_THISSOLVID;
1799   di->state = di_enterrepo;
1800 }
1801
1802 int
1803 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1804 {
1805   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1806     return 0;
1807   if (!ma)
1808     return 1;
1809   return datamatcher_match(ma, di->kv.str);
1810 }
1811
1812 /************************************************************************
1813  * data modify functions
1814  */
1815
1816 /* extend repodata so that it includes solvables p */
1817 void
1818 repodata_extend(Repodata *data, Id p)
1819 {
1820   if (data->start == data->end)
1821     data->start = data->end = p;
1822   if (p >= data->end)
1823     {
1824       int old = data->end - data->start;
1825       int new = p - data->end + 1;
1826       if (data->attrs)
1827         {
1828           data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1829           memset(data->attrs + old, 0, new * sizeof(Id *));
1830         }
1831       data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1832       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1833       data->end = p + 1;
1834     }
1835   if (p < data->start)
1836     {
1837       int old = data->end - data->start;
1838       int new = data->start - p;
1839       if (data->attrs)
1840         {
1841           data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1842           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1843           memset(data->attrs, 0, new * sizeof(Id *));
1844         }
1845       data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1846       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1847       memset(data->incoreoffset, 0, new * sizeof(Id));
1848       data->start = p;
1849     }
1850 }
1851
1852 /* shrink end of repodata */
1853 void
1854 repodata_shrink(Repodata *data, int end)
1855 {
1856   int i;
1857
1858   if (data->end <= end)
1859     return;
1860   if (data->start >= end)
1861     {
1862       if (data->attrs)
1863         {
1864           for (i = 0; i < data->end - data->start; i++)
1865             solv_free(data->attrs[i]);
1866           data->attrs = solv_free(data->attrs);
1867         }
1868       data->incoreoffset = solv_free(data->incoreoffset);
1869       data->start = data->end = 0;
1870       return;
1871     }
1872   if (data->attrs)
1873     {
1874       for (i = end; i < data->end; i++)
1875         solv_free(data->attrs[i - data->start]);
1876       data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
1877     }
1878   if (data->incoreoffset)
1879     data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
1880   data->end = end;
1881 }
1882
1883 /* extend repodata so that it includes solvables from start to start + num - 1 */
1884 void
1885 repodata_extend_block(Repodata *data, Id start, Id num)
1886 {
1887   if (!num)
1888     return;
1889   if (!data->incoreoffset)
1890     {
1891       data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1892       data->start = start;
1893       data->end = start + num;
1894       return;
1895     }
1896   repodata_extend(data, start);
1897   if (num > 1)
1898     repodata_extend(data, start + num - 1);
1899 }
1900
1901 /**********************************************************************/
1902
1903
1904 #define REPODATA_ATTRS_BLOCK 31
1905 #define REPODATA_ATTRDATA_BLOCK 1023
1906 #define REPODATA_ATTRIDDATA_BLOCK 63
1907 #define REPODATA_ATTRNUM64DATA_BLOCK 15
1908
1909
1910 Id
1911 repodata_new_handle(Repodata *data)
1912 {
1913   if (!data->nxattrs)
1914     {
1915       data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1916       data->nxattrs = 2;        /* -1: SOLVID_META */
1917     }
1918   data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1919   data->xattrs[data->nxattrs] = 0;
1920   return -(data->nxattrs++);
1921 }
1922
1923 static inline Id **
1924 repodata_get_attrp(Repodata *data, Id handle)
1925 {
1926   if (handle < 0)
1927     {
1928       if (handle == SOLVID_META && !data->xattrs)
1929         {
1930           data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1931           data->nxattrs = 2;
1932         }
1933       return data->xattrs - handle;
1934     }
1935   if (handle < data->start || handle >= data->end)
1936     repodata_extend(data, handle);
1937   if (!data->attrs)
1938     data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
1939   return data->attrs + (handle - data->start);
1940 }
1941
1942 static void
1943 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1944 {
1945   Id *pp;
1946   Id *ap, **app;
1947   int i;
1948
1949   app = repodata_get_attrp(data, handle);
1950   ap = *app;
1951   i = 0;
1952   if (ap)
1953     {
1954       /* Determine equality based on the name only, allows us to change
1955          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1956       for (pp = ap; *pp; pp += 2)
1957         if (data->keys[*pp].name == data->keys[keyid].name)
1958           break;
1959       if (*pp)
1960         {
1961           if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
1962             {
1963               pp[0] = keyid;
1964               pp[1] = val;
1965             }
1966           return;
1967         }
1968       i = pp - ap;
1969     }
1970   ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1971   *app = ap;
1972   pp = ap + i;
1973   *pp++ = keyid;
1974   *pp++ = val;
1975   *pp = 0;
1976 }
1977
1978
1979 static void
1980 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
1981 {
1982   Id keyid;
1983
1984   keyid = repodata_key2id(data, key, 1);
1985   repodata_insert_keyid(data, solvid, keyid, val, 1);
1986 }
1987
1988 void
1989 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
1990 {
1991   Repokey key;
1992   key.name = keyname;
1993   key.type = REPOKEY_TYPE_ID;
1994   key.size = 0;
1995   key.storage = KEY_STORAGE_INCORE;
1996   repodata_set(data, solvid, &key, id);
1997 }
1998
1999 void
2000 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
2001 {
2002   Repokey key;
2003   key.name = keyname;
2004   key.type = REPOKEY_TYPE_NUM;
2005   key.size = 0;
2006   key.storage = KEY_STORAGE_INCORE;
2007   if (num >= 0x80000000)
2008     {
2009       data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
2010       data->attrnum64data[data->attrnum64datalen] = num;
2011       num = 0x80000000 | data->attrnum64datalen++;
2012     }
2013   repodata_set(data, solvid, &key, (Id)num);
2014 }
2015
2016 void
2017 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
2018 {
2019   Repokey key;
2020   Id id;
2021   if (data->localpool)
2022     id = stringpool_str2id(&data->spool, str, 1);
2023   else
2024     id = pool_str2id(data->repo->pool, str, 1);
2025   key.name = keyname;
2026   key.type = REPOKEY_TYPE_ID;
2027   key.size = 0;
2028   key.storage = KEY_STORAGE_INCORE;
2029   repodata_set(data, solvid, &key, id);
2030 }
2031
2032 void
2033 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
2034 {
2035   Repokey key;
2036   key.name = keyname;
2037   key.type = REPOKEY_TYPE_CONSTANT;
2038   key.size = constant;
2039   key.storage = KEY_STORAGE_INCORE;
2040   repodata_set(data, solvid, &key, 0);
2041 }
2042
2043 void
2044 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
2045 {
2046   Repokey key;
2047   key.name = keyname;
2048   key.type = REPOKEY_TYPE_CONSTANTID;
2049   key.size = id;
2050   key.storage = KEY_STORAGE_INCORE;
2051   repodata_set(data, solvid, &key, 0);
2052 }
2053
2054 void
2055 repodata_set_void(Repodata *data, Id solvid, Id keyname)
2056 {
2057   Repokey key;
2058   key.name = keyname;
2059   key.type = REPOKEY_TYPE_VOID;
2060   key.size = 0;
2061   key.storage = KEY_STORAGE_INCORE;
2062   repodata_set(data, solvid, &key, 0);
2063 }
2064
2065 void
2066 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
2067 {
2068   Repokey key;
2069   int l;
2070
2071   l = strlen(str) + 1;
2072   key.name = keyname;
2073   key.type = REPOKEY_TYPE_STR;
2074   key.size = 0;
2075   key.storage = KEY_STORAGE_INCORE;
2076   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2077   memcpy(data->attrdata + data->attrdatalen, str, l);
2078   repodata_set(data, solvid, &key, data->attrdatalen);
2079   data->attrdatalen += l;
2080 }
2081
2082 void
2083 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
2084 {
2085   Repokey key;
2086   unsigned char *dp;
2087
2088   if (len < 0)
2089     return;
2090   key.name = keyname;
2091   key.type = REPOKEY_TYPE_BINARY;
2092   key.size = 0;
2093   key.storage = KEY_STORAGE_INCORE;
2094   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
2095   dp = data->attrdata + data->attrdatalen;
2096   if (len >= (1 << 14))
2097     {
2098       if (len >= (1 << 28))
2099         *dp++ = (len >> 28) | 128;
2100       if (len >= (1 << 21))
2101         *dp++ = (len >> 21) | 128;
2102       *dp++ = (len >> 14) | 128;
2103     }
2104   if (len >= (1 << 7))
2105     *dp++ = (len >> 7) | 128;
2106   *dp++ = len & 127;
2107   if (len)
2108     memcpy(dp, buf, len);
2109   repodata_set(data, solvid, &key, data->attrdatalen);
2110   data->attrdatalen = dp + len - data->attrdata;
2111 }
2112
2113 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
2114  * so that the caller can append entrysize new elements plus the termination zero there */
2115 static void
2116 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2117 {
2118   int oldsize;
2119   Id *ida, *pp, **ppp;
2120
2121   /* check if it is the same as last time, this speeds things up a lot */
2122   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2123     {
2124       /* great! just append the new data */
2125       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2126       data->attriddatalen--;    /* overwrite terminating 0  */
2127       data->lastdatalen += entrysize;
2128       return;
2129     }
2130
2131   ppp = repodata_get_attrp(data, handle);
2132   pp = *ppp;
2133   if (pp)
2134     {
2135       for (; *pp; pp += 2)
2136         if (data->keys[*pp].name == keyname)
2137           break;
2138     }
2139   if (!pp || !*pp || data->keys[*pp].type != keytype)
2140     {
2141       /* not found. allocate new key */
2142       Repokey key;
2143       Id keyid;
2144       key.name = keyname;
2145       key.type = keytype;
2146       key.size = 0;
2147       key.storage = KEY_STORAGE_INCORE;
2148       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2149       keyid = repodata_key2id(data, &key, 1);
2150       repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
2151       data->lasthandle = handle;
2152       data->lastkey = keyid;
2153       data->lastdatalen = data->attriddatalen + entrysize + 1;
2154       return;
2155     }
2156   oldsize = 0;
2157   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2158     oldsize += entrysize;
2159   if (ida + 1 == data->attriddata + data->attriddatalen)
2160     {
2161       /* this was the last entry, just append it */
2162       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2163       data->attriddatalen--;    /* overwrite terminating 0  */
2164     }
2165   else
2166     {
2167       /* too bad. move to back. */
2168       data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2169       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2170       pp[1] = data->attriddatalen;
2171       data->attriddatalen += oldsize;
2172     }
2173   data->lasthandle = handle;
2174   data->lastkey = *pp;
2175   data->lastdatalen = data->attriddatalen + entrysize + 1;
2176 }
2177
2178 void
2179 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2180                       const unsigned char *str)
2181 {
2182   Repokey key;
2183   int l;
2184
2185   if (!(l = solv_chksum_len(type)))
2186     return;
2187   key.name = keyname;
2188   key.type = type;
2189   key.size = 0;
2190   key.storage = KEY_STORAGE_INCORE;
2191   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2192   memcpy(data->attrdata + data->attrdatalen, str, l);
2193   repodata_set(data, solvid, &key, data->attrdatalen);
2194   data->attrdatalen += l;
2195 }
2196
2197 void
2198 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2199                       const char *str)
2200 {
2201   unsigned char buf[64];
2202   int l;
2203
2204   if (!(l = solv_chksum_len(type)))
2205     return;
2206   if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
2207     return;
2208   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2209 }
2210
2211 const char *
2212 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2213 {
2214   int l;
2215
2216   if (!(l = solv_chksum_len(type)))
2217     return "";
2218   return pool_bin2hex(data->repo->pool, buf, l);
2219 }
2220
2221 /* rpm filenames don't contain the epoch, so strip it */
2222 static inline const char *
2223 evrid2vrstr(Pool *pool, Id evrid)
2224 {
2225   const char *p, *evr = pool_id2str(pool, evrid);
2226   if (!evr)
2227     return evr;
2228   for (p = evr; *p >= '0' && *p <= '9'; p++)
2229     ;
2230   return p != evr && *p == ':' && p[1] ? p + 1 : evr;
2231 }
2232
2233 void
2234 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2235 {
2236   Pool *pool = data->repo->pool;
2237   Solvable *s;
2238   const char *str, *fp;
2239   int l = 0;
2240
2241   if (medianr)
2242     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2243   if (!dir)
2244     {
2245       if ((dir = strrchr(file, '/')) != 0)
2246         {
2247           l = dir - file;
2248           dir = file;
2249           file = dir + l + 1;
2250           if (!l)
2251             l++;
2252         }
2253     }
2254   else
2255     l = strlen(dir);
2256   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2257     {
2258       dir += 2;
2259       l -= 2;
2260     }
2261   if (l == 1 && dir[0] == '.')
2262     l = 0;
2263   s = pool->solvables + solvid;
2264   if (dir && l)
2265     {
2266       str = pool_id2str(pool, s->arch);
2267       if (!strncmp(dir, str, l) && !str[l])
2268         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2269       else if (!dir[l])
2270         repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir);
2271       else
2272         {
2273           char *dir2 = solv_strdup(dir);
2274           dir2[l] = 0;
2275           repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir2);
2276           free(dir2);
2277         }
2278     }
2279   fp = file;
2280   str = pool_id2str(pool, s->name);
2281   l = strlen(str);
2282   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2283     {
2284       fp += l + 1;
2285       str = evrid2vrstr(pool, s->evr);
2286       l = strlen(str);
2287       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2288         {
2289           fp += l + 1;
2290           str = pool_id2str(pool, s->arch);
2291           l = strlen(str);
2292           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2293             {
2294               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2295               return;
2296             }
2297         }
2298     }
2299   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2300 }
2301
2302 void
2303 repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
2304 {
2305   Pool *pool = data->repo->pool;
2306   Solvable *s = pool->solvables + solvid;
2307   const char *p, *sevr, *sarch, *name, *evr;
2308
2309   p = strrchr(sourcepkg, '.');
2310   if (!p || strcmp(p, ".rpm") != 0)
2311     {
2312       if (*sourcepkg)
2313         repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
2314       return;
2315     }
2316   p--;
2317   while (p > sourcepkg && *p != '.')
2318     p--;
2319   if (*p != '.' || p == sourcepkg)
2320     return;
2321   sarch = p-- + 1;
2322   while (p > sourcepkg && *p != '-')
2323     p--;
2324   if (*p != '-' || p == sourcepkg)
2325     return;
2326   p--;
2327   while (p > sourcepkg && *p != '-')
2328     p--;
2329   if (*p != '-' || p == sourcepkg)
2330     return;
2331   sevr = p + 1;
2332   pool = s->repo->pool;
2333
2334   name = pool_id2str(pool, s->name);
2335   if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
2336     repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
2337   else
2338     repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
2339
2340   evr = evrid2vrstr(pool, s->evr);
2341   if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
2342     repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
2343   else
2344     repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
2345
2346   if (!strcmp(sarch, "src.rpm"))
2347     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
2348   else if (!strcmp(sarch, "nosrc.rpm"))
2349     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
2350   else
2351     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
2352 }
2353
2354 void
2355 repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
2356 {
2357   Repokey key;
2358   int i;
2359
2360   key.name = keyname;
2361   key.type = REPOKEY_TYPE_IDARRAY;
2362   key.size = 0;
2363   key.storage = KEY_STORAGE_INCORE;
2364   repodata_set(data, solvid, &key, data->attriddatalen);
2365   data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2366   for (i = 0; i < q->count; i++)
2367     data->attriddata[data->attriddatalen++] = q->elements[i];
2368   data->attriddata[data->attriddatalen++] = 0;
2369 }
2370
2371 void
2372 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2373 {
2374   assert(dir);
2375 #if 0
2376 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2377 #endif
2378   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2379   data->attriddata[data->attriddatalen++] = dir;
2380   data->attriddata[data->attriddatalen++] = num;
2381   data->attriddata[data->attriddatalen++] = num2;
2382   data->attriddata[data->attriddatalen++] = 0;
2383 }
2384
2385 void
2386 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2387 {
2388   Id stroff;
2389   int l;
2390
2391   assert(dir);
2392   l = strlen(str) + 1;
2393   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2394   memcpy(data->attrdata + data->attrdatalen, str, l);
2395   stroff = data->attrdatalen;
2396   data->attrdatalen += l;
2397
2398 #if 0
2399 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2400 #endif
2401   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2402   data->attriddata[data->attriddatalen++] = dir;
2403   data->attriddata[data->attriddatalen++] = stroff;
2404   data->attriddata[data->attriddatalen++] = 0;
2405 }
2406
2407 void
2408 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2409 {
2410 #if 0
2411 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2412 #endif
2413   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2414   data->attriddata[data->attriddatalen++] = id;
2415   data->attriddata[data->attriddatalen++] = 0;
2416 }
2417
2418 void
2419 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2420                            const char *str)
2421 {
2422   Id id;
2423   if (data->localpool)
2424     id = stringpool_str2id(&data->spool, str, 1);
2425   else
2426     id = pool_str2id(data->repo->pool, str, 1);
2427   repodata_add_idarray(data, solvid, keyname, id);
2428 }
2429
2430 void
2431 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2432 {
2433   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2434   data->attriddata[data->attriddatalen++] = ghandle;
2435   data->attriddata[data->attriddatalen++] = 0;
2436 }
2437
2438 void
2439 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2440 {
2441   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2442   data->attriddata[data->attriddatalen++] = ghandle;
2443   data->attriddata[data->attriddatalen++] = 0;
2444 }
2445
2446 void
2447 repodata_delete_uninternalized(Repodata *data, Id solvid, Id keyname)
2448 {
2449   Id *pp, *ap, **app;
2450   app = repodata_get_attrp(data, solvid);
2451   ap = *app;
2452   if (!ap)
2453     return;
2454   for (; *ap; ap += 2)
2455     if (data->keys[*ap].name == keyname)
2456       break;
2457   if (!*ap)
2458     return;
2459   pp = ap;
2460   ap += 2;
2461   for (; *ap; ap += 2)
2462     {
2463       if (data->keys[*ap].name == keyname)
2464         continue;
2465       *pp++ = ap[0];
2466       *pp++ = ap[1];
2467     }
2468   *pp = 0;
2469 }
2470
2471 /* XXX: does not work correctly, needs fix in iterators! */
2472 void
2473 repodata_delete(Repodata *data, Id solvid, Id keyname)
2474 {
2475   Repokey key;
2476   key.name = keyname;
2477   key.type = REPOKEY_TYPE_DELETED;
2478   key.size = 0;
2479   key.storage = KEY_STORAGE_INCORE;
2480   repodata_set(data, solvid, &key, 0);
2481 }
2482
2483 /* add all (uninternalized) attrs from src to dest */
2484 void
2485 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2486 {
2487   Id *keyp;
2488   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2489     return;
2490   for (; *keyp; keyp += 2)
2491     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2492 }
2493
2494 /* add some (uninternalized) attrs from src to dest */
2495 void
2496 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2497 {
2498   Id *keyp;
2499   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2500     return;
2501   for (; *keyp; keyp += 2)
2502     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2503       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2504 }
2505
2506 /* swap (uninternalized) attrs from src and dest */
2507 void
2508 repodata_swap_attrs(Repodata *data, Id dest, Id src)
2509 {
2510   Id *tmpattrs;
2511   if (!data->attrs || dest == src)
2512     return;
2513   tmpattrs = data->attrs[dest - data->start];
2514   data->attrs[dest - data->start] = data->attrs[src - data->start];
2515   data->attrs[src - data->start] = tmpattrs;
2516 }
2517
2518
2519 /**********************************************************************/
2520
2521 /* TODO: unify with repo_write and repo_solv! */
2522
2523 #define EXTDATA_BLOCK 1023
2524
2525 struct extdata {
2526   unsigned char *buf;
2527   int len;
2528 };
2529
2530 static void
2531 data_addid(struct extdata *xd, Id sx)
2532 {
2533   unsigned int x = (unsigned int)sx;
2534   unsigned char *dp;
2535
2536   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2537   dp = xd->buf + xd->len;
2538
2539   if (x >= (1 << 14))
2540     {
2541       if (x >= (1 << 28))
2542         *dp++ = (x >> 28) | 128;
2543       if (x >= (1 << 21))
2544         *dp++ = (x >> 21) | 128;
2545       *dp++ = (x >> 14) | 128;
2546     }
2547   if (x >= (1 << 7))
2548     *dp++ = (x >> 7) | 128;
2549   *dp++ = x & 127;
2550   xd->len = dp - xd->buf;
2551 }
2552
2553 static void
2554 data_addid64(struct extdata *xd, unsigned long long x)
2555 {
2556   if (x >= 0x100000000)
2557     {
2558       if ((x >> 35) != 0)
2559         {
2560           data_addid(xd, (Id)(x >> 35));
2561           xd->buf[xd->len - 1] |= 128;
2562         }
2563       data_addid(xd, (Id)((unsigned int)x | 0x80000000));
2564       xd->buf[xd->len - 5] = (x >> 28) | 128;
2565     }
2566   else
2567     data_addid(xd, (Id)x);
2568 }
2569
2570 static void
2571 data_addideof(struct extdata *xd, Id sx, int eof)
2572 {
2573   unsigned int x = (unsigned int)sx;
2574   unsigned char *dp;
2575
2576   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2577   dp = xd->buf + xd->len;
2578
2579   if (x >= (1 << 13))
2580     {
2581       if (x >= (1 << 27))
2582         *dp++ = (x >> 27) | 128;
2583       if (x >= (1 << 20))
2584         *dp++ = (x >> 20) | 128;
2585       *dp++ = (x >> 13) | 128;
2586     }
2587   if (x >= (1 << 6))
2588     *dp++ = (x >> 6) | 128;
2589   *dp++ = eof ? (x & 63) : (x & 63) | 64;
2590   xd->len = dp - xd->buf;
2591 }
2592
2593 static void
2594 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2595 {
2596   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2597   memcpy(xd->buf + xd->len, blob, len);
2598   xd->len += len;
2599 }
2600
2601 /*********************************/
2602
2603 /* internalalize some key into incore/vincore data */
2604
2605 static void
2606 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2607                        struct extdata *newvincore,
2608                        Id *schema,
2609                        Repokey *key, Id val)
2610 {
2611   Id *ida;
2612   struct extdata *xd;
2613   unsigned int oldvincorelen = 0;
2614   Id schemaid, *sp;
2615
2616   xd = newincore;
2617   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2618     {
2619       xd = newvincore;
2620       oldvincorelen = xd->len;
2621     }
2622   switch (key->type)
2623     {
2624     case REPOKEY_TYPE_VOID:
2625     case REPOKEY_TYPE_CONSTANT:
2626     case REPOKEY_TYPE_CONSTANTID:
2627       break;
2628     case REPOKEY_TYPE_STR:
2629       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2630       break;
2631     case REPOKEY_TYPE_MD5:
2632       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2633       break;
2634     case REPOKEY_TYPE_SHA1:
2635       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2636       break;
2637     case REPOKEY_TYPE_SHA256:
2638       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2639       break;
2640     case REPOKEY_TYPE_NUM:
2641       if (val & 0x80000000)
2642         {
2643           data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
2644           break;
2645         }
2646       /* FALLTHROUGH */
2647     case REPOKEY_TYPE_ID:
2648     case REPOKEY_TYPE_DIR:
2649       data_addid(xd, val);
2650       break;
2651     case REPOKEY_TYPE_BINARY:
2652       {
2653         Id len;
2654         unsigned char *dp = data_read_id(data->attrdata + val, &len);
2655         dp += (unsigned int)len;
2656         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
2657       }
2658       break;
2659     case REPOKEY_TYPE_IDARRAY:
2660       for (ida = data->attriddata + val; *ida; ida++)
2661         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2662       break;
2663     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2664       for (ida = data->attriddata + val; *ida; ida += 3)
2665         {
2666           data_addid(xd, ida[0]);
2667           data_addid(xd, ida[1]);
2668           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2669         }
2670       break;
2671     case REPOKEY_TYPE_DIRSTRARRAY:
2672       for (ida = data->attriddata + val; *ida; ida += 2)
2673         {
2674           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2675           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2676         }
2677       break;
2678     case REPOKEY_TYPE_FIXARRAY:
2679       {
2680         int num = 0;
2681         schemaid = 0;
2682         for (ida = data->attriddata + val; *ida; ida++)
2683           {
2684             Id *kp;
2685             sp = schema;
2686             kp = data->xattrs[-*ida];
2687             if (!kp)
2688               continue;
2689             num++;
2690             for (;*kp; kp += 2)
2691               *sp++ = *kp;
2692             *sp = 0;
2693             if (!schemaid)
2694               schemaid = repodata_schema2id(data, schema, 1);
2695             else if (schemaid != repodata_schema2id(data, schema, 0))
2696               {
2697                 pool_debug(data->repo->pool, SOLV_FATAL, "fixarray substructs with different schemas\n");
2698                 exit(1);
2699               }
2700           }
2701         if (!num)
2702           break;
2703         data_addid(xd, num);
2704         data_addid(xd, schemaid);
2705         for (ida = data->attriddata + val; *ida; ida++)
2706           {
2707             Id *kp = data->xattrs[-*ida];
2708             if (!kp)
2709               continue;
2710             for (;*kp; kp += 2)
2711               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
2712           }
2713         break;
2714       }
2715     case REPOKEY_TYPE_FLEXARRAY:
2716       {
2717         int num = 0;
2718         for (ida = data->attriddata + val; *ida; ida++)
2719           num++;
2720         data_addid(xd, num);
2721         for (ida = data->attriddata + val; *ida; ida++)
2722           {
2723             Id *kp = data->xattrs[-*ida];
2724             if (!kp)
2725               {
2726                 data_addid(xd, 0);      /* XXX */
2727                 continue;
2728               }
2729             sp = schema;
2730             for (;*kp; kp += 2)
2731               *sp++ = *kp;
2732             *sp = 0;
2733             schemaid = repodata_schema2id(data, schema, 1);
2734             data_addid(xd, schemaid);
2735             kp = data->xattrs[-*ida];
2736             for (;*kp; kp += 2)
2737               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
2738           }
2739         break;
2740       }
2741     default:
2742       pool_debug(data->repo->pool, SOLV_FATAL, "don't know how to handle type %d\n", key->type);
2743       exit(1);
2744     }
2745   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2746     {
2747       /* put offset/len in incore */
2748       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2749       oldvincorelen = xd->len - oldvincorelen;
2750       data_addid(newincore, oldvincorelen);
2751     }
2752 }
2753
2754 void
2755 repodata_internalize(Repodata *data)
2756 {
2757   Repokey *key, solvkey;
2758   Id entry, nentry;
2759   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2760   unsigned char *dp, *ndp;
2761   int newschema, oldcount;
2762   struct extdata newincore;
2763   struct extdata newvincore;
2764   Id solvkeyid;
2765
2766   if (!data->attrs && !data->xattrs)
2767     return;
2768
2769   newvincore.buf = data->vincore;
2770   newvincore.len = data->vincorelen;
2771
2772   /* find the solvables key, create if needed */
2773   memset(&solvkey, 0, sizeof(solvkey));
2774   solvkey.name = REPOSITORY_SOLVABLES;
2775   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2776   solvkey.size = 0;
2777   solvkey.storage = KEY_STORAGE_INCORE;
2778   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2779
2780   schema = solv_malloc2(data->nkeys, sizeof(Id));
2781   seen = solv_malloc2(data->nkeys, sizeof(Id));
2782
2783   /* Merge the data already existing (in data->schemata, ->incoredata and
2784      friends) with the new attributes in data->attrs[].  */
2785   nentry = data->end - data->start;
2786   memset(&newincore, 0, sizeof(newincore));
2787   data_addid(&newincore, 0);    /* start data at offset 1 */
2788
2789   data->mainschema = 0;
2790   data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
2791
2792   /* join entry data */
2793   /* we start with the meta data, entry -1 */
2794   for (entry = -1; entry < nentry; entry++)
2795     {
2796       memset(seen, 0, data->nkeys * sizeof(Id));
2797       oldschema = 0;
2798       dp = data->incoredata;
2799       if (dp)
2800         {
2801           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2802           dp = data_read_id(dp, &oldschema);
2803         }
2804 #if 0
2805 fprintf(stderr, "oldschema %d\n", oldschema);
2806 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2807 fprintf(stderr, "schemadata %p\n", data->schemadata);
2808 #endif
2809       /* seen: -1: old data  0: skipped  >0: id + 1 */
2810       newschema = 0;
2811       oldcount = 0;
2812       sp = schema;
2813       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2814         {
2815           if (seen[*keyp])
2816             {
2817               pool_debug(data->repo->pool, SOLV_FATAL, "Inconsistent old data (key occured twice).\n");
2818               exit(1);
2819             }
2820           seen[*keyp] = -1;
2821           *sp++ = *keyp;
2822           oldcount++;
2823         }
2824       if (entry >= 0)
2825         keyp = data->attrs ? data->attrs[entry] : 0;
2826       else
2827         {
2828           /* strip solvables key */
2829           *sp = 0;
2830           for (sp = keyp = schema; *sp; sp++)
2831             if (*sp != solvkeyid)
2832               *keyp++ = *sp;
2833             else
2834               oldcount--;
2835           sp = keyp;
2836           seen[solvkeyid] = 0;
2837           keyp = data->xattrs ? data->xattrs[1] : 0;
2838         }
2839       if (keyp)
2840         for (; *keyp; keyp += 2)
2841           {
2842             if (!seen[*keyp])
2843               {
2844                 newschema = 1;
2845                 *sp++ = *keyp;
2846               }
2847             seen[*keyp] = keyp[1] + 1;
2848           }
2849       if (entry < 0 && data->end != data->start)
2850         {
2851           *sp++ = solvkeyid;
2852           newschema = 1;
2853         }
2854       *sp = 0;
2855       if (newschema)
2856         /* Ideally we'd like to sort the new schema here, to ensure
2857            schema equality independend of the ordering.  We can't do that
2858            yet.  For once see below (old ids need to come before new ids).
2859            An additional difficulty is that we also need to move
2860            the values with the keys.  */
2861         schemaid = repodata_schema2id(data, schema, 1);
2862       else
2863         schemaid = oldschema;
2864
2865
2866       /* Now create data blob.  We walk through the (possibly new) schema
2867          and either copy over old data, or insert the new.  */
2868       /* XXX Here we rely on the fact that the (new) schema has the form
2869          o1 o2 o3 o4 ... | n1 n2 n3 ...
2870          (oX being the old keyids (possibly overwritten), and nX being
2871           the new keyids).  This rules out sorting the keyids in order
2872          to ensure a small schema count.  */
2873       if (entry >= 0)
2874         data->incoreoffset[entry] = newincore.len;
2875       data_addid(&newincore, schemaid);
2876       if (entry == -1)
2877         {
2878           data->mainschema = schemaid;
2879           data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
2880         }
2881       keypstart = data->schemadata + data->schemata[schemaid];
2882       for (keyp = keypstart; *keyp; keyp++)
2883         {
2884           if (entry == -1)
2885             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2886           if (*keyp == solvkeyid)
2887             {
2888               /* add flexarray entry count */
2889               data_addid(&newincore, data->end - data->start);
2890               break;
2891             }
2892           key = data->keys + *keyp;
2893 #if 0
2894           fprintf(stderr, "internalize %d(%d):%s:%s\n", entry, entry + data->start, pool_id2str(data->repo->pool, key->name), pool_id2str(data->repo->pool, key->type));
2895 #endif
2896           ndp = dp;
2897           if (oldcount)
2898             {
2899               /* Skip the data associated with this old key.  */
2900               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2901                 {
2902                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2903                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2904                 }
2905               else if (key->storage == KEY_STORAGE_INCORE)
2906                 ndp = data_skip_key(data, dp, key);
2907               oldcount--;
2908             }
2909           if (seen[*keyp] == -1)
2910             {
2911               /* If this key was an old one _and_ was not overwritten with
2912                  a different value copy over the old value (we skipped it
2913                  above).  */
2914               if (dp != ndp)
2915                 data_addblob(&newincore, dp, ndp - dp);
2916               seen[*keyp] = 0;
2917             }
2918           else if (seen[*keyp])
2919             {
2920               /* Otherwise we have a new value.  Parse it into the internal
2921                  form.  */
2922               repodata_serialize_key(data, &newincore, &newvincore,
2923                                      schema, key, seen[*keyp] - 1);
2924             }
2925           dp = ndp;
2926         }
2927       if (entry >= 0 && data->attrs && data->attrs[entry])
2928         data->attrs[entry] = solv_free(data->attrs[entry]);
2929     }
2930   /* free all xattrs */
2931   for (entry = 0; entry < data->nxattrs; entry++)
2932     if (data->xattrs[entry])
2933       solv_free(data->xattrs[entry]);
2934   data->xattrs = solv_free(data->xattrs);
2935   data->nxattrs = 0;
2936
2937   data->lasthandle = 0;
2938   data->lastkey = 0;
2939   data->lastdatalen = 0;
2940   solv_free(schema);
2941   solv_free(seen);
2942   repodata_free_schemahash(data);
2943
2944   solv_free(data->incoredata);
2945   data->incoredata = newincore.buf;
2946   data->incoredatalen = newincore.len;
2947   data->incoredatafree = 0;
2948
2949   solv_free(data->vincore);
2950   data->vincore = newvincore.buf;
2951   data->vincorelen = newvincore.len;
2952
2953   data->attrs = solv_free(data->attrs);
2954   data->attrdata = solv_free(data->attrdata);
2955   data->attriddata = solv_free(data->attriddata);
2956   data->attrnum64data = solv_free(data->attrnum64data);
2957   data->attrdatalen = 0;
2958   data->attriddatalen = 0;
2959   data->attrnum64datalen = 0;
2960 }
2961
2962 void
2963 repodata_disable_paging(Repodata *data)
2964 {
2965   if (maybe_load_repodata(data, 0))
2966     repopagestore_disable_paging(&data->store);
2967 }
2968
2969 static void
2970 repodata_load_stub(Repodata *data)
2971 {
2972   Repo *repo = data->repo;
2973   Pool *pool = repo->pool;
2974   int r, i;
2975   struct _Pool_tmpspace oldtmpspace;
2976
2977   if (!pool->loadcallback)
2978     {
2979       data->state = REPODATA_ERROR;
2980       return;
2981     }
2982   data->state = REPODATA_LOADING;
2983
2984   /* save tmp space */
2985   oldtmpspace = pool->tmpspace;
2986   memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
2987
2988   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
2989
2990   /* restore tmp space */
2991   for (i = 0; i < POOL_TMPSPACEBUF; i++)
2992     solv_free(pool->tmpspace.buf[i]);
2993   pool->tmpspace = oldtmpspace;
2994
2995   data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
2996 }
2997
2998 void
2999 repodata_create_stubs(Repodata *data)
3000 {
3001   Repo *repo = data->repo;
3002   Pool *pool = repo->pool;
3003   Repodata *sdata;
3004   int *stubdataids;
3005   Dataiterator di;
3006   Id xkeyname = 0;
3007   int i, cnt = 0;
3008   int repodataid;
3009   int datastart, dataend;
3010
3011   repodataid = data - repo->repodata;
3012   datastart = data->start;
3013   dataend = data->end;
3014   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3015   while (dataiterator_step(&di))
3016     {
3017       if (di.data - repo->repodata != repodataid)
3018         continue;
3019       cnt++;
3020     }
3021   dataiterator_free(&di);
3022   if (!cnt)
3023     return;
3024   stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
3025   for (i = 0; i < cnt; i++)
3026     {
3027       sdata = repo_add_repodata(repo, 0);
3028       if (dataend > datastart)
3029         repodata_extend_block(sdata, datastart, dataend - datastart);
3030       stubdataids[i] = sdata - repo->repodata;
3031       sdata->state = REPODATA_STUB;
3032       sdata->loadcallback = repodata_load_stub;
3033     }
3034   i = 0;
3035   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3036   sdata = 0;
3037   while (dataiterator_step(&di))
3038     {
3039       if (di.data - repo->repodata != repodataid)
3040         continue;
3041       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
3042         {
3043           dataiterator_entersub(&di);
3044           sdata = repo->repodata + stubdataids[i++];
3045           xkeyname = 0;
3046           continue;
3047         }
3048       switch (di.key->type)
3049         {
3050         case REPOKEY_TYPE_ID:
3051           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
3052           break;
3053         case REPOKEY_TYPE_CONSTANTID:
3054           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
3055           break;
3056         case REPOKEY_TYPE_STR:
3057           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
3058           break;
3059         case REPOKEY_TYPE_VOID:
3060           repodata_set_void(sdata, SOLVID_META, di.key->name);
3061           break;
3062         case REPOKEY_TYPE_NUM:
3063           repodata_set_num(sdata, SOLVID_META, di.key->name, SOLV_KV_NUM64(&di.kv));
3064           break;
3065         case REPOKEY_TYPE_MD5:
3066         case REPOKEY_TYPE_SHA1:
3067         case REPOKEY_TYPE_SHA256:
3068           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
3069           break;
3070         case REPOKEY_TYPE_IDARRAY:
3071           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
3072           if (di.key->name == REPOSITORY_KEYS)
3073             {
3074               Repokey xkey;
3075
3076               if (!xkeyname)
3077                 {
3078                   if (!di.kv.eof)
3079                     xkeyname = di.kv.id;
3080                   continue;
3081                 }
3082               xkey.name = xkeyname;
3083               xkey.type = di.kv.id;
3084               xkey.storage = KEY_STORAGE_INCORE;
3085               xkey.size = 0;
3086               repodata_key2id(sdata, &xkey, 1);
3087               xkeyname = 0;
3088             }
3089         default:
3090           break;
3091         }
3092     }
3093   dataiterator_free(&di);
3094   for (i = 0; i < cnt; i++)
3095     repodata_internalize(repo->repodata + stubdataids[i]);
3096   solv_free(stubdataids);
3097 }
3098
3099 unsigned int
3100 repodata_memused(Repodata *data)
3101 {
3102   return data->incoredatalen + data->vincorelen;
3103 }
3104
3105 /*
3106 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
3107 */