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