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