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