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