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