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