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