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