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