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