- advance num64 support. For now, we store the high 32 bits in kv->num2 to stay somew...
[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 int *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 = kv.num2 ? ~(unsigned int)0 : kv.num;
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.entry = 0;
1550               di->state = di_nextsolvablekey;
1551               break;
1552             }
1553           di->kv.entry = -1;
1554           /* FALLTHROUGH */
1555
1556         case di_nextsolvableattr:
1557           di->state = di_nextsolvableattr;
1558           di->kv.id = *di->idp++;
1559           di->kv.entry++;
1560           if (!*di->idp)
1561             {
1562               di->kv.eof = 1;
1563               di->state = di_nextsolvablekey;
1564             }
1565           break;
1566
1567         }
1568
1569       if (di->matcher.match)
1570         {
1571           /* simple pre-check so that we don't need to stringify */
1572           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))
1573             {
1574               int l = strlen(di->matcher.match) - strlen(di->kv.str);
1575               if (l < 0 || strcmp(di->matcher.match + l, di->kv.str))
1576                 continue;
1577             }
1578           if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1579             {
1580               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1581                 return 1;
1582               continue;
1583             }
1584           if (!datamatcher_match(&di->matcher, di->kv.str))
1585             continue;
1586         }
1587       /* found something! */
1588       return 1;
1589     }
1590 }
1591
1592 void
1593 dataiterator_entersub(Dataiterator *di)
1594 {
1595   if (di->state == di_nextarrayelement)
1596     di->state = di_entersub;
1597 }
1598
1599 void
1600 dataiterator_setpos(Dataiterator *di)
1601 {
1602   if (di->kv.eof == 2)
1603     {
1604       pool_clear_pos(di->pool);
1605       return;
1606     }
1607   di->pool->pos.solvid = di->solvid;
1608   di->pool->pos.repo = di->repo;
1609   di->pool->pos.repodataid = di->data - di->repo->repodata;
1610   di->pool->pos.schema = di->kv.id;
1611   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1612 }
1613
1614 void
1615 dataiterator_setpos_parent(Dataiterator *di)
1616 {
1617   if (!di->kv.parent || di->kv.parent->eof == 2)
1618     {
1619       pool_clear_pos(di->pool);
1620       return;
1621     }
1622   di->pool->pos.solvid = di->solvid;
1623   di->pool->pos.repo = di->repo;
1624   di->pool->pos.repodataid = di->data - di->repo->repodata;
1625   di->pool->pos.schema = di->kv.parent->id;
1626   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1627 }
1628
1629 /* clones just the position, not the search keys/matcher */
1630 void
1631 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
1632 {
1633   di->state = from->state;
1634   di->flags &= ~SEARCH_THISSOLVID;
1635   di->flags |= (from->flags & SEARCH_THISSOLVID);
1636   di->repo = from->repo;
1637   di->data = from->data;
1638   di->dp = from->dp;
1639   di->ddp = from->ddp;
1640   di->idp = from->idp;
1641   di->keyp = from->keyp;
1642   di->key = from->key;
1643   di->kv = from->kv;
1644   di->repodataid = from->repodataid;
1645   di->solvid = from->solvid;
1646   di->repoid = from->repoid;
1647   di->rootlevel = from->rootlevel;
1648   memcpy(di->parents, from->parents, sizeof(from->parents));
1649   di->nparents = from->nparents;
1650   if (di->nparents)
1651     {
1652       int i;
1653       for (i = 1; i < di->nparents; i++)
1654         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1655       di->kv.parent = &di->parents[di->nparents - 1].kv;
1656     }
1657 }
1658
1659 void
1660 dataiterator_seek(Dataiterator *di, int whence)
1661 {
1662   if ((whence & DI_SEEK_STAY) != 0)
1663     di->rootlevel = di->nparents;
1664   switch (whence & ~DI_SEEK_STAY)
1665     {
1666     case DI_SEEK_CHILD:
1667       if (di->state != di_nextarrayelement)
1668         break;
1669       if ((whence & DI_SEEK_STAY) != 0)
1670         di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
1671       di->state = di_entersub;
1672       break;
1673     case DI_SEEK_PARENT:
1674       if (!di->nparents)
1675         {
1676           di->state = di_bye;
1677           break;
1678         }
1679       di->nparents--;
1680       if (di->rootlevel > di->nparents)
1681         di->rootlevel = di->nparents;
1682       di->dp = di->parents[di->nparents].dp;
1683       di->kv = di->parents[di->nparents].kv;
1684       di->keyp = di->parents[di->nparents].keyp;
1685       di->key = di->data->keys + *di->keyp;
1686       di->ddp = (unsigned char *)di->kv.str;
1687       di->keyname = di->keynames[di->nparents - di->rootlevel];
1688       di->state = di_nextarrayelement;
1689       break;
1690     case DI_SEEK_REWIND:
1691       if (!di->nparents)
1692         {
1693           di->state = di_bye;
1694           break;
1695         }
1696       di->dp = (unsigned char *)di->kv.parent->str;
1697       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
1698       di->state = di_enterschema;
1699       break;
1700     default:
1701       break;
1702     }
1703 }
1704
1705 void
1706 dataiterator_skip_attribute(Dataiterator *di)
1707 {
1708   if (di->state == di_nextsolvableattr)
1709     di->state = di_nextsolvablekey;
1710   else
1711     di->state = di_nextkey;
1712 }
1713
1714 void
1715 dataiterator_skip_solvable(Dataiterator *di)
1716 {
1717   di->nparents = 0;
1718   di->kv.parent = 0;
1719   di->rootlevel = 0;
1720   di->keyname = di->keynames[0];
1721   di->state = di_nextsolvable;
1722 }
1723
1724 void
1725 dataiterator_skip_repo(Dataiterator *di)
1726 {
1727   di->nparents = 0;
1728   di->kv.parent = 0;
1729   di->rootlevel = 0;
1730   di->keyname = di->keynames[0];
1731   di->state = di_nextrepo;
1732 }
1733
1734 void
1735 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
1736 {
1737   di->nparents = 0;
1738   di->kv.parent = 0;
1739   di->rootlevel = 0;
1740   di->keyname = di->keynames[0];
1741   if (solvid == SOLVID_POS)
1742     {
1743       di->repo = di->pool->pos.repo;
1744       if (!di->repo)
1745         {
1746           di->state = di_bye;
1747           return;
1748         }
1749       di->repoid = 0;
1750       di->data = di->repo->repodata + di->pool->pos.repodataid;
1751       di->repodataid = 0;
1752       di->solvid = solvid;
1753       di->state = di_enterrepo;
1754       di->flags |= SEARCH_THISSOLVID;
1755       return;
1756     }
1757   if (solvid > 0)
1758     {
1759       di->repo = di->pool->solvables[solvid].repo;
1760       di->repoid = 0;
1761     }
1762   else if (di->repoid > 0)
1763     {
1764       if (!di->pool->urepos)
1765         {
1766           di->state = di_bye;
1767           return;
1768         }
1769       di->repoid = 1;
1770       di->repo = di->pool->repos[di->repoid];
1771     }
1772   di->repodataid = 1;
1773   di->solvid = solvid;
1774   if (solvid)
1775     di->flags |= SEARCH_THISSOLVID;
1776   di->state = di_enterrepo;
1777 }
1778
1779 void
1780 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1781 {
1782   di->nparents = 0;
1783   di->kv.parent = 0;
1784   di->rootlevel = 0;
1785   di->repo = repo;
1786   di->repoid = 0;       /* 0 means stay at repo */
1787   di->repodataid = 1;
1788   di->solvid = 0;
1789   di->flags &= ~SEARCH_THISSOLVID;
1790   di->state = di_enterrepo;
1791 }
1792
1793 int
1794 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1795 {
1796   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1797     return 0;
1798   if (!ma)
1799     return 1;
1800   return datamatcher_match(ma, di->kv.str);
1801 }
1802
1803 /************************************************************************
1804  * data modify functions
1805  */
1806
1807 /* extend repodata so that it includes solvables p */
1808 void
1809 repodata_extend(Repodata *data, Id p)
1810 {
1811   if (data->start == data->end)
1812     data->start = data->end = p;
1813   if (p >= data->end)
1814     {
1815       int old = data->end - data->start;
1816       int new = p - data->end + 1;
1817       if (data->attrs)
1818         {
1819           data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1820           memset(data->attrs + old, 0, new * sizeof(Id *));
1821         }
1822       data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1823       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1824       data->end = p + 1;
1825     }
1826   if (p < data->start)
1827     {
1828       int old = data->end - data->start;
1829       int new = data->start - p;
1830       if (data->attrs)
1831         {
1832           data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1833           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1834           memset(data->attrs, 0, new * sizeof(Id *));
1835         }
1836       data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1837       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1838       memset(data->incoreoffset, 0, new * sizeof(Id));
1839       data->start = p;
1840     }
1841 }
1842
1843 /* shrink end of repodata */
1844 void
1845 repodata_shrink(Repodata *data, int end)
1846 {
1847   int i;
1848
1849   if (data->end <= end)
1850     return;
1851   if (data->start >= end)
1852     {
1853       if (data->attrs)
1854         {
1855           for (i = 0; i < data->end - data->start; i++)
1856             solv_free(data->attrs[i]);
1857           data->attrs = solv_free(data->attrs);
1858         }
1859       data->incoreoffset = solv_free(data->incoreoffset);
1860       data->start = data->end = 0;
1861       return;
1862     }
1863   if (data->attrs)
1864     {
1865       for (i = end; i < data->end; i++)
1866         solv_free(data->attrs[i - data->start]);
1867       data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
1868     }
1869   if (data->incoreoffset)
1870     data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
1871   data->end = end;
1872 }
1873
1874 /* extend repodata so that it includes solvables from start to start + num - 1 */
1875 void
1876 repodata_extend_block(Repodata *data, Id start, Id num)
1877 {
1878   if (!num)
1879     return;
1880   if (!data->incoreoffset)
1881     {
1882       data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
1883       data->start = start;
1884       data->end = start + num;
1885       return;
1886     }
1887   repodata_extend(data, start);
1888   if (num > 1)
1889     repodata_extend(data, start + num - 1);
1890 }
1891
1892 /**********************************************************************/
1893
1894
1895 #define REPODATA_ATTRS_BLOCK 31
1896 #define REPODATA_ATTRDATA_BLOCK 1023
1897 #define REPODATA_ATTRIDDATA_BLOCK 63
1898 #define REPODATA_ATTRNUM64DATA_BLOCK 15
1899
1900
1901 Id
1902 repodata_new_handle(Repodata *data)
1903 {
1904   if (!data->nxattrs)
1905     {
1906       data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1907       data->nxattrs = 2;        /* -1: SOLVID_META */
1908     }
1909   data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
1910   data->xattrs[data->nxattrs] = 0;
1911   return -(data->nxattrs++);
1912 }
1913
1914 static inline Id **
1915 repodata_get_attrp(Repodata *data, Id handle)
1916 {
1917   if (handle < 0)
1918     {
1919       if (handle == SOLVID_META && !data->xattrs)
1920         {
1921           data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
1922           data->nxattrs = 2;
1923         }
1924       return data->xattrs - handle;
1925     }
1926   if (handle < data->start || handle >= data->end)
1927     repodata_extend(data, handle);
1928   if (!data->attrs)
1929     data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
1930   return data->attrs + (handle - data->start);
1931 }
1932
1933 static void
1934 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
1935 {
1936   Id *pp;
1937   Id *ap, **app;
1938   int i;
1939
1940   app = repodata_get_attrp(data, handle);
1941   ap = *app;
1942   i = 0;
1943   if (ap)
1944     {
1945       /* Determine equality based on the name only, allows us to change
1946          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
1947       for (pp = ap; *pp; pp += 2)
1948         if (data->keys[*pp].name == data->keys[keyid].name)
1949           break;
1950       if (*pp)
1951         {
1952           if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
1953             {
1954               pp[0] = keyid;
1955               pp[1] = val;
1956             }
1957           return;
1958         }
1959       i = pp - ap;
1960     }
1961   ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
1962   *app = ap;
1963   pp = ap + i;
1964   *pp++ = keyid;
1965   *pp++ = val;
1966   *pp = 0;
1967 }
1968
1969
1970 static void
1971 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
1972 {
1973   Id keyid;
1974
1975   keyid = repodata_key2id(data, key, 1);
1976   repodata_insert_keyid(data, solvid, keyid, val, 1);
1977 }
1978
1979 void
1980 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
1981 {
1982   Repokey key;
1983   key.name = keyname;
1984   key.type = REPOKEY_TYPE_ID;
1985   key.size = 0;
1986   key.storage = KEY_STORAGE_INCORE;
1987   repodata_set(data, solvid, &key, id);
1988 }
1989
1990 void
1991 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned int num)
1992 {
1993   Repokey key;
1994   key.name = keyname;
1995   key.type = REPOKEY_TYPE_NUM;
1996   key.size = 0;
1997   key.storage = KEY_STORAGE_INCORE;
1998   if (num >= 0x80000000)
1999     {
2000       data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
2001       data->attrnum64data[data->attrnum64datalen] = num;
2002       num = 0x80000000 | data->attrnum64datalen++;
2003     }
2004   repodata_set(data, solvid, &key, (Id)num);
2005 }
2006
2007 void
2008 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
2009 {
2010   Repokey key;
2011   Id id;
2012   if (data->localpool)
2013     id = stringpool_str2id(&data->spool, str, 1);
2014   else
2015     id = pool_str2id(data->repo->pool, str, 1);
2016   key.name = keyname;
2017   key.type = REPOKEY_TYPE_ID;
2018   key.size = 0;
2019   key.storage = KEY_STORAGE_INCORE;
2020   repodata_set(data, solvid, &key, id);
2021 }
2022
2023 void
2024 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
2025 {
2026   Repokey key;
2027   key.name = keyname;
2028   key.type = REPOKEY_TYPE_CONSTANT;
2029   key.size = constant;
2030   key.storage = KEY_STORAGE_INCORE;
2031   repodata_set(data, solvid, &key, 0);
2032 }
2033
2034 void
2035 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
2036 {
2037   Repokey key;
2038   key.name = keyname;
2039   key.type = REPOKEY_TYPE_CONSTANTID;
2040   key.size = id;
2041   key.storage = KEY_STORAGE_INCORE;
2042   repodata_set(data, solvid, &key, 0);
2043 }
2044
2045 void
2046 repodata_set_void(Repodata *data, Id solvid, Id keyname)
2047 {
2048   Repokey key;
2049   key.name = keyname;
2050   key.type = REPOKEY_TYPE_VOID;
2051   key.size = 0;
2052   key.storage = KEY_STORAGE_INCORE;
2053   repodata_set(data, solvid, &key, 0);
2054 }
2055
2056 void
2057 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
2058 {
2059   Repokey key;
2060   int l;
2061
2062   l = strlen(str) + 1;
2063   key.name = keyname;
2064   key.type = REPOKEY_TYPE_STR;
2065   key.size = 0;
2066   key.storage = KEY_STORAGE_INCORE;
2067   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2068   memcpy(data->attrdata + data->attrdatalen, str, l);
2069   repodata_set(data, solvid, &key, data->attrdatalen);
2070   data->attrdatalen += l;
2071 }
2072
2073 void
2074 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
2075 {
2076   Repokey key;
2077   unsigned char *dp;
2078
2079   if (len < 0)
2080     return;
2081   key.name = keyname;
2082   key.type = REPOKEY_TYPE_BINARY;
2083   key.size = 0;
2084   key.storage = KEY_STORAGE_INCORE;
2085   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
2086   dp = data->attrdata + data->attrdatalen;
2087   if (len >= (1 << 14))
2088     {
2089       if (len >= (1 << 28))
2090         *dp++ = (len >> 28) | 128;
2091       if (len >= (1 << 21))
2092         *dp++ = (len >> 21) | 128;
2093       *dp++ = (len >> 14) | 128;
2094     }
2095   if (len >= (1 << 7))
2096     *dp++ = (len >> 7) | 128;
2097   *dp++ = len & 127;
2098   if (len)
2099     memcpy(dp, buf, len);
2100   repodata_set(data, solvid, &key, data->attrdatalen);
2101   data->attrdatalen = dp + len - data->attrdata;
2102 }
2103
2104 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
2105  * so that the caller can append entrysize new elements plus the termination zero there */
2106 static void
2107 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2108 {
2109   int oldsize;
2110   Id *ida, *pp, **ppp;
2111
2112   /* check if it is the same as last time, this speeds things up a lot */
2113   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2114     {
2115       /* great! just append the new data */
2116       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2117       data->attriddatalen--;    /* overwrite terminating 0  */
2118       data->lastdatalen += entrysize;
2119       return;
2120     }
2121
2122   ppp = repodata_get_attrp(data, handle);
2123   pp = *ppp;
2124   if (pp)
2125     {
2126       for (; *pp; pp += 2)
2127         if (data->keys[*pp].name == keyname)
2128           break;
2129     }
2130   if (!pp || !*pp || data->keys[*pp].type != keytype)
2131     {
2132       /* not found. allocate new key */
2133       Repokey key;
2134       Id keyid;
2135       key.name = keyname;
2136       key.type = keytype;
2137       key.size = 0;
2138       key.storage = KEY_STORAGE_INCORE;
2139       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2140       keyid = repodata_key2id(data, &key, 1);
2141       repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
2142       data->lasthandle = handle;
2143       data->lastkey = keyid;
2144       data->lastdatalen = data->attriddatalen + entrysize + 1;
2145       return;
2146     }
2147   oldsize = 0;
2148   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2149     oldsize += entrysize;
2150   if (ida + 1 == data->attriddata + data->attriddatalen)
2151     {
2152       /* this was the last entry, just append it */
2153       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2154       data->attriddatalen--;    /* overwrite terminating 0  */
2155     }
2156   else
2157     {
2158       /* too bad. move to back. */
2159       data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2160       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2161       pp[1] = data->attriddatalen;
2162       data->attriddatalen += oldsize;
2163     }
2164   data->lasthandle = handle;
2165   data->lastkey = *pp;
2166   data->lastdatalen = data->attriddatalen + entrysize + 1;
2167 }
2168
2169 void
2170 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2171                       const unsigned char *str)
2172 {
2173   Repokey key;
2174   int l;
2175
2176   if (!(l = solv_chksum_len(type)))
2177     return;
2178   key.name = keyname;
2179   key.type = type;
2180   key.size = 0;
2181   key.storage = KEY_STORAGE_INCORE;
2182   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2183   memcpy(data->attrdata + data->attrdatalen, str, l);
2184   repodata_set(data, solvid, &key, data->attrdatalen);
2185   data->attrdatalen += l;
2186 }
2187
2188 void
2189 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2190                       const char *str)
2191 {
2192   unsigned char buf[64];
2193   int l;
2194
2195   if (!(l = solv_chksum_len(type)))
2196     return;
2197   if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
2198     return;
2199   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2200 }
2201
2202 const char *
2203 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2204 {
2205   int l;
2206
2207   if (!(l = solv_chksum_len(type)))
2208     return "";
2209   return pool_bin2hex(data->repo->pool, buf, l);
2210 }
2211
2212 /* rpm filenames don't contain the epoch, so strip it */
2213 static inline const char *
2214 evrid2vrstr(Pool *pool, Id evrid)
2215 {
2216   const char *p, *evr = pool_id2str(pool, evrid);
2217   if (!evr)
2218     return evr;
2219   for (p = evr; *p >= '0' && *p <= '9'; p++)
2220     ;
2221   return p != evr && *p == ':' ? p + 1 : evr;
2222 }
2223
2224 void
2225 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2226 {
2227   Pool *pool = data->repo->pool;
2228   Solvable *s;
2229   const char *str, *fp;
2230   int l = 0;
2231
2232   if (medianr)
2233     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2234   if (!dir)
2235     {
2236       if ((dir = strrchr(file, '/')) != 0)
2237         {
2238           l = dir - file;
2239           dir = file;
2240           file = dir + l + 1;
2241           if (!l)
2242             l++;
2243         }
2244     }
2245   else
2246     l = strlen(dir);
2247   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2248     {
2249       dir += 2;
2250       l -= 2;
2251     }
2252   if (l == 1 && dir[0] == '.')
2253     l = 0;
2254   s = pool->solvables + solvid;
2255   if (dir && l)
2256     {
2257       str = pool_id2str(pool, s->arch);
2258       if (!strncmp(dir, str, l) && !str[l])
2259         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2260       else if (!dir[l])
2261         repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir);
2262       else
2263         {
2264           char *dir2 = solv_strdup(dir);
2265           dir2[l] = 0;
2266           repodata_set_str(data, solvid, SOLVABLE_MEDIADIR, dir2);
2267           free(dir2);
2268         }
2269     }
2270   fp = file;
2271   str = pool_id2str(pool, s->name);
2272   l = strlen(str);
2273   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2274     {
2275       fp += l + 1;
2276       str = evrid2vrstr(pool, s->evr);
2277       l = strlen(str);
2278       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2279         {
2280           fp += l + 1;
2281           str = pool_id2str(pool, s->arch);
2282           l = strlen(str);
2283           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2284             {
2285               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2286               return;
2287             }
2288         }
2289     }
2290   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2291 }
2292
2293 void
2294 repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
2295 {
2296   Repokey key;
2297   int i;
2298
2299   key.name = keyname;
2300   key.type = REPOKEY_TYPE_IDARRAY;
2301   key.size = 0;
2302   key.storage = KEY_STORAGE_INCORE;
2303   repodata_set(data, solvid, &key, data->attriddatalen);
2304   data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2305   for (i = 0; i < q->count; i++)
2306     data->attriddata[data->attriddatalen++] = q->elements[i];
2307   data->attriddata[data->attriddatalen++] = 0;
2308 }
2309
2310 void
2311 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2312 {
2313   assert(dir);
2314 #if 0
2315 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2316 #endif
2317   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2318   data->attriddata[data->attriddatalen++] = dir;
2319   data->attriddata[data->attriddatalen++] = num;
2320   data->attriddata[data->attriddatalen++] = num2;
2321   data->attriddata[data->attriddatalen++] = 0;
2322 }
2323
2324 void
2325 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2326 {
2327   Id stroff;
2328   int l;
2329
2330   assert(dir);
2331   l = strlen(str) + 1;
2332   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2333   memcpy(data->attrdata + data->attrdatalen, str, l);
2334   stroff = data->attrdatalen;
2335   data->attrdatalen += l;
2336
2337 #if 0
2338 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2339 #endif
2340   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2341   data->attriddata[data->attriddatalen++] = dir;
2342   data->attriddata[data->attriddatalen++] = stroff;
2343   data->attriddata[data->attriddatalen++] = 0;
2344 }
2345
2346 void
2347 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2348 {
2349 #if 0
2350 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2351 #endif
2352   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2353   data->attriddata[data->attriddatalen++] = id;
2354   data->attriddata[data->attriddatalen++] = 0;
2355 }
2356
2357 void
2358 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2359                            const char *str)
2360 {
2361   Id id;
2362   if (data->localpool)
2363     id = stringpool_str2id(&data->spool, str, 1);
2364   else
2365     id = pool_str2id(data->repo->pool, str, 1);
2366   repodata_add_idarray(data, solvid, keyname, id);
2367 }
2368
2369 void
2370 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2371 {
2372   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2373   data->attriddata[data->attriddatalen++] = ghandle;
2374   data->attriddata[data->attriddatalen++] = 0;
2375 }
2376
2377 void
2378 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2379 {
2380   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2381   data->attriddata[data->attriddatalen++] = ghandle;
2382   data->attriddata[data->attriddatalen++] = 0;
2383 }
2384
2385 void
2386 repodata_delete_uninternalized(Repodata *data, Id solvid, Id keyname)
2387 {
2388   Id *pp, *ap, **app;
2389   app = repodata_get_attrp(data, solvid);
2390   ap = *app;
2391   if (!ap)
2392     return;
2393   for (; *ap; ap += 2)
2394     if (data->keys[*ap].name == keyname)
2395       break;
2396   if (!*ap)
2397     return;
2398   pp = ap;
2399   ap += 2;
2400   for (; *ap; ap += 2)
2401     {
2402       if (data->keys[*ap].name == keyname)
2403         continue;
2404       *pp++ = ap[0];
2405       *pp++ = ap[1];
2406     }
2407   *pp = 0;
2408 }
2409
2410 /* XXX: does not work correctly, needs fix in iterators! */
2411 void
2412 repodata_delete(Repodata *data, Id solvid, Id keyname)
2413 {
2414   Repokey key;
2415   key.name = keyname;
2416   key.type = REPOKEY_TYPE_DELETED;
2417   key.size = 0;
2418   key.storage = KEY_STORAGE_INCORE;
2419   repodata_set(data, solvid, &key, 0);
2420 }
2421
2422 /* add all (uninternalized) attrs from src to dest */
2423 void
2424 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2425 {
2426   Id *keyp;
2427   if (dest == src || data->attrs || !(keyp = data->attrs[src - data->start]))
2428     return;
2429   for (; *keyp; keyp += 2)
2430     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2431 }
2432
2433 /* add some (uninternalized) attrs from src to dest */
2434 void
2435 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2436 {
2437   Id *keyp;
2438   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2439     return;
2440   for (; *keyp; keyp += 2)
2441     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2442       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2443 }
2444
2445 /* swap (uninternalized) attrs from src and dest */
2446 void
2447 repodata_swap_attrs(Repodata *data, Id dest, Id src)
2448 {
2449   Id *tmpattrs;
2450   if (!data->attrs || dest == src)
2451     return;
2452   tmpattrs = data->attrs[dest - data->start];
2453   data->attrs[dest - data->start] = data->attrs[src - data->start];
2454   data->attrs[src - data->start] = tmpattrs;
2455 }
2456
2457
2458 /**********************************************************************/
2459
2460 /* TODO: unify with repo_write and repo_solv! */
2461
2462 #define EXTDATA_BLOCK 1023
2463
2464 struct extdata {
2465   unsigned char *buf;
2466   int len;
2467 };
2468
2469 static void
2470 data_addid(struct extdata *xd, Id sx)
2471 {
2472   unsigned char *dp;
2473   unsigned int x = (unsigned int)sx;
2474
2475   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2476   dp = xd->buf + xd->len;
2477
2478   if (x >= (1 << 14))
2479     {
2480       if (x >= (1 << 28))
2481         *dp++ = (x >> 28) | 128;
2482       if (x >= (1 << 21))
2483         *dp++ = (x >> 21) | 128;
2484       *dp++ = (x >> 14) | 128;
2485     }
2486   if (x >= (1 << 7))
2487     *dp++ = (x >> 7) | 128;
2488   *dp++ = x & 127;
2489   xd->len = dp - xd->buf;
2490 }
2491
2492 static void
2493 data_addid64(struct extdata *xd, unsigned long long x)
2494 {
2495   if (x >= 0x100000000)
2496     {
2497       if ((x >> 35) != 0)
2498         {
2499           data_addid(xd, (Id)(x >> 35));
2500           xd->buf[xd->len - 1] |= 128;
2501         }
2502       data_addid(xd, (Id)((unsigned int)x | 0x80000000));
2503       xd->buf[xd->len - 5] = (x >> 28) | 128;
2504     }
2505   else
2506     data_addid(xd, (Id)x);
2507 }
2508
2509 static void
2510 data_addideof(struct extdata *xd, Id x, int eof)
2511 {
2512   if (x < 0)
2513     abort();    /* XXX: not yet */
2514   if (x >= 64)
2515     x = (x & 63) | ((x & ~63) << 1);
2516   data_addid(xd, (eof ? x : x | 64));
2517 }
2518
2519 static void
2520 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2521 {
2522   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2523   memcpy(xd->buf + xd->len, blob, len);
2524   xd->len += len;
2525 }
2526
2527 /*********************************/
2528
2529 /* internalalize some key into incore/vincore data */
2530
2531 static void
2532 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2533                        struct extdata *newvincore,
2534                        Id *schema,
2535                        Repokey *key, Id val)
2536 {
2537   Id *ida;
2538   struct extdata *xd;
2539   unsigned int oldvincorelen = 0;
2540   Id schemaid, *sp;
2541
2542   xd = newincore;
2543   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2544     {
2545       xd = newvincore;
2546       oldvincorelen = xd->len;
2547     }
2548   switch (key->type)
2549     {
2550     case REPOKEY_TYPE_VOID:
2551     case REPOKEY_TYPE_CONSTANT:
2552     case REPOKEY_TYPE_CONSTANTID:
2553       break;
2554     case REPOKEY_TYPE_STR:
2555       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2556       break;
2557     case REPOKEY_TYPE_MD5:
2558       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2559       break;
2560     case REPOKEY_TYPE_SHA1:
2561       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2562       break;
2563     case REPOKEY_TYPE_SHA256:
2564       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2565       break;
2566     case REPOKEY_TYPE_NUM:
2567       if (val & 0x80000000)
2568         {
2569           data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
2570           break;
2571         }
2572       /* FALLTHROUGH */
2573     case REPOKEY_TYPE_ID:
2574     case REPOKEY_TYPE_DIR:
2575       data_addid(xd, val);
2576       break;
2577     case REPOKEY_TYPE_BINARY:
2578       {
2579         Id len;
2580         unsigned char *dp = data_read_id(data->attrdata + val, &len);
2581         dp += len;
2582         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
2583       }
2584       break;
2585     case REPOKEY_TYPE_IDARRAY:
2586       for (ida = data->attriddata + val; *ida; ida++)
2587         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2588       break;
2589     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2590       for (ida = data->attriddata + val; *ida; ida += 3)
2591         {
2592           data_addid(xd, ida[0]);
2593           data_addid(xd, ida[1]);
2594           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2595         }
2596       break;
2597     case REPOKEY_TYPE_DIRSTRARRAY:
2598       for (ida = data->attriddata + val; *ida; ida += 2)
2599         {
2600           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2601           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2602         }
2603       break;
2604     case REPOKEY_TYPE_FIXARRAY:
2605       {
2606         int num = 0;
2607         schemaid = 0;
2608         for (ida = data->attriddata + val; *ida; ida++)
2609           {
2610             Id *kp;
2611             sp = schema;
2612             kp = data->xattrs[-*ida];
2613             if (!kp)
2614               continue;
2615             num++;
2616             for (;*kp; kp += 2)
2617               *sp++ = *kp;
2618             *sp = 0;
2619             if (!schemaid)
2620               schemaid = repodata_schema2id(data, schema, 1);
2621             else if (schemaid != repodata_schema2id(data, schema, 0))
2622               {
2623                 pool_debug(data->repo->pool, SOLV_FATAL, "fixarray substructs with different schemas\n");
2624                 exit(1);
2625               }
2626           }
2627         if (!num)
2628           break;
2629         data_addid(xd, num);
2630         data_addid(xd, schemaid);
2631         for (ida = data->attriddata + val; *ida; ida++)
2632           {
2633             Id *kp = data->xattrs[-*ida];
2634             if (!kp)
2635               continue;
2636             for (;*kp; kp += 2)
2637               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
2638           }
2639         break;
2640       }
2641     case REPOKEY_TYPE_FLEXARRAY:
2642       {
2643         int num = 0;
2644         for (ida = data->attriddata + val; *ida; ida++)
2645           num++;
2646         data_addid(xd, num);
2647         for (ida = data->attriddata + val; *ida; ida++)
2648           {
2649             Id *kp = data->xattrs[-*ida];
2650             if (!kp)
2651               {
2652                 data_addid(xd, 0);      /* XXX */
2653                 continue;
2654               }
2655             sp = schema;
2656             for (;*kp; kp += 2)
2657               *sp++ = *kp;
2658             *sp = 0;
2659             schemaid = repodata_schema2id(data, schema, 1);
2660             data_addid(xd, schemaid);
2661             kp = data->xattrs[-*ida];
2662             for (;*kp; kp += 2)
2663               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
2664           }
2665         break;
2666       }
2667     default:
2668       pool_debug(data->repo->pool, SOLV_FATAL, "don't know how to handle type %d\n", key->type);
2669       exit(1);
2670     }
2671   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2672     {
2673       /* put offset/len in incore */
2674       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2675       oldvincorelen = xd->len - oldvincorelen;
2676       data_addid(newincore, oldvincorelen);
2677     }
2678 }
2679
2680 void
2681 repodata_internalize(Repodata *data)
2682 {
2683   Repokey *key, solvkey;
2684   Id entry, nentry;
2685   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2686   unsigned char *dp, *ndp;
2687   int newschema, oldcount;
2688   struct extdata newincore;
2689   struct extdata newvincore;
2690   Id solvkeyid;
2691
2692   if (!data->attrs && !data->xattrs)
2693     return;
2694
2695   newvincore.buf = data->vincore;
2696   newvincore.len = data->vincorelen;
2697
2698   /* find the solvables key, create if needed */
2699   memset(&solvkey, 0, sizeof(solvkey));
2700   solvkey.name = REPOSITORY_SOLVABLES;
2701   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
2702   solvkey.size = 0;
2703   solvkey.storage = KEY_STORAGE_INCORE;
2704   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
2705
2706   schema = solv_malloc2(data->nkeys, sizeof(Id));
2707   seen = solv_malloc2(data->nkeys, sizeof(Id));
2708
2709   /* Merge the data already existing (in data->schemata, ->incoredata and
2710      friends) with the new attributes in data->attrs[].  */
2711   nentry = data->end - data->start;
2712   memset(&newincore, 0, sizeof(newincore));
2713   data_addid(&newincore, 0);    /* start data at offset 1 */
2714
2715   data->mainschema = 0;
2716   data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
2717
2718   /* join entry data */
2719   /* we start with the meta data, entry -1 */
2720   for (entry = -1; entry < nentry; entry++)
2721     {
2722       memset(seen, 0, data->nkeys * sizeof(Id));
2723       oldschema = 0;
2724       dp = data->incoredata;
2725       if (dp)
2726         {
2727           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
2728           dp = data_read_id(dp, &oldschema);
2729         }
2730 #if 0
2731 fprintf(stderr, "oldschema %d\n", oldschema);
2732 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
2733 fprintf(stderr, "schemadata %p\n", data->schemadata);
2734 #endif
2735       /* seen: -1: old data  0: skipped  >0: id + 1 */
2736       newschema = 0;
2737       oldcount = 0;
2738       sp = schema;
2739       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
2740         {
2741           if (seen[*keyp])
2742             {
2743               pool_debug(data->repo->pool, SOLV_FATAL, "Inconsistent old data (key occured twice).\n");
2744               exit(1);
2745             }
2746           seen[*keyp] = -1;
2747           *sp++ = *keyp;
2748           oldcount++;
2749         }
2750       if (entry >= 0)
2751         keyp = data->attrs ? data->attrs[entry] : 0;
2752       else
2753         {
2754           /* strip solvables key */
2755           *sp = 0;
2756           for (sp = keyp = schema; *sp; sp++)
2757             if (*sp != solvkeyid)
2758               *keyp++ = *sp;
2759             else
2760               oldcount--;
2761           sp = keyp;
2762           seen[solvkeyid] = 0;
2763           keyp = data->xattrs ? data->xattrs[1] : 0;
2764         }
2765       if (keyp)
2766         for (; *keyp; keyp += 2)
2767           {
2768             if (!seen[*keyp])
2769               {
2770                 newschema = 1;
2771                 *sp++ = *keyp;
2772               }
2773             seen[*keyp] = keyp[1] + 1;
2774           }
2775       if (entry < 0 && data->end != data->start)
2776         {
2777           *sp++ = solvkeyid;
2778           newschema = 1;
2779         }
2780       *sp = 0;
2781       if (newschema)
2782         /* Ideally we'd like to sort the new schema here, to ensure
2783            schema equality independend of the ordering.  We can't do that
2784            yet.  For once see below (old ids need to come before new ids).
2785            An additional difficulty is that we also need to move
2786            the values with the keys.  */
2787         schemaid = repodata_schema2id(data, schema, 1);
2788       else
2789         schemaid = oldschema;
2790
2791
2792       /* Now create data blob.  We walk through the (possibly new) schema
2793          and either copy over old data, or insert the new.  */
2794       /* XXX Here we rely on the fact that the (new) schema has the form
2795          o1 o2 o3 o4 ... | n1 n2 n3 ...
2796          (oX being the old keyids (possibly overwritten), and nX being
2797           the new keyids).  This rules out sorting the keyids in order
2798          to ensure a small schema count.  */
2799       if (entry >= 0)
2800         data->incoreoffset[entry] = newincore.len;
2801       data_addid(&newincore, schemaid);
2802       if (entry == -1)
2803         {
2804           data->mainschema = schemaid;
2805           data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
2806         }
2807       keypstart = data->schemadata + data->schemata[schemaid];
2808       for (keyp = keypstart; *keyp; keyp++)
2809         {
2810           if (entry == -1)
2811             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
2812           if (*keyp == solvkeyid)
2813             {
2814               /* add flexarray entry count */
2815               data_addid(&newincore, data->end - data->start);
2816               break;
2817             }
2818           key = data->keys + *keyp;
2819 #if 0
2820           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));
2821 #endif
2822           ndp = dp;
2823           if (oldcount)
2824             {
2825               /* Skip the data associated with this old key.  */
2826               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2827                 {
2828                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
2829                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
2830                 }
2831               else if (key->storage == KEY_STORAGE_INCORE)
2832                 ndp = data_skip_key(data, dp, key);
2833               oldcount--;
2834             }
2835           if (seen[*keyp] == -1)
2836             {
2837               /* If this key was an old one _and_ was not overwritten with
2838                  a different value copy over the old value (we skipped it
2839                  above).  */
2840               if (dp != ndp)
2841                 data_addblob(&newincore, dp, ndp - dp);
2842               seen[*keyp] = 0;
2843             }
2844           else if (seen[*keyp])
2845             {
2846               /* Otherwise we have a new value.  Parse it into the internal
2847                  form.  */
2848               repodata_serialize_key(data, &newincore, &newvincore,
2849                                      schema, key, seen[*keyp] - 1);
2850             }
2851           dp = ndp;
2852         }
2853       if (entry >= 0 && data->attrs && data->attrs[entry])
2854         data->attrs[entry] = solv_free(data->attrs[entry]);
2855     }
2856   /* free all xattrs */
2857   for (entry = 0; entry < data->nxattrs; entry++)
2858     if (data->xattrs[entry])
2859       solv_free(data->xattrs[entry]);
2860   data->xattrs = solv_free(data->xattrs);
2861   data->nxattrs = 0;
2862
2863   data->lasthandle = 0;
2864   data->lastkey = 0;
2865   data->lastdatalen = 0;
2866   solv_free(schema);
2867   solv_free(seen);
2868   repodata_free_schemahash(data);
2869
2870   solv_free(data->incoredata);
2871   data->incoredata = newincore.buf;
2872   data->incoredatalen = newincore.len;
2873   data->incoredatafree = 0;
2874
2875   solv_free(data->vincore);
2876   data->vincore = newvincore.buf;
2877   data->vincorelen = newvincore.len;
2878
2879   data->attrs = solv_free(data->attrs);
2880   data->attrdata = solv_free(data->attrdata);
2881   data->attriddata = solv_free(data->attriddata);
2882   data->attrnum64data = solv_free(data->attrnum64data);
2883   data->attrdatalen = 0;
2884   data->attriddatalen = 0;
2885   data->attrnum64datalen = 0;
2886 }
2887
2888 void
2889 repodata_disable_paging(Repodata *data)
2890 {
2891   if (maybe_load_repodata(data, 0))
2892     repopagestore_disable_paging(&data->store);
2893 }
2894
2895 static void
2896 repodata_load_stub(Repodata *data)
2897 {
2898   Repo *repo = data->repo;
2899   Pool *pool = repo->pool;
2900   int r, i;
2901   struct _Pool_tmpspace oldtmpspace;
2902
2903   if (!pool->loadcallback)
2904     {
2905       data->state = REPODATA_ERROR;
2906       return;
2907     }
2908   data->state = REPODATA_LOADING;
2909
2910   /* save tmp space */
2911   oldtmpspace = pool->tmpspace;
2912   memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
2913
2914   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
2915
2916   /* restore tmp space */
2917   for (i = 0; i < POOL_TMPSPACEBUF; i++)
2918     solv_free(pool->tmpspace.buf[i]);
2919   pool->tmpspace = oldtmpspace;
2920
2921   data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
2922 }
2923
2924 void
2925 repodata_create_stubs(Repodata *data)
2926 {
2927   Repo *repo = data->repo;
2928   Pool *pool = repo->pool;
2929   Repodata *sdata;
2930   int *stubdataids;
2931   Dataiterator di;
2932   Id xkeyname = 0;
2933   int i, cnt = 0;
2934   int repodataid;
2935   int datastart, dataend;
2936
2937   repodataid = data - repo->repodata;
2938   datastart = data->start;
2939   dataend = data->end;
2940   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
2941   while (dataiterator_step(&di))
2942     {
2943       if (di.data - repo->repodata != repodataid)
2944         continue;
2945       cnt++;
2946     }
2947   dataiterator_free(&di);
2948   if (!cnt)
2949     return;
2950   stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
2951   for (i = 0; i < cnt; i++)
2952     {
2953       sdata = repo_add_repodata(repo, 0);
2954       if (dataend > datastart)
2955         repodata_extend_block(sdata, datastart, dataend - datastart);
2956       stubdataids[i] = sdata - repo->repodata;
2957       sdata->state = REPODATA_STUB;
2958       sdata->loadcallback = repodata_load_stub;
2959     }
2960   i = 0;
2961   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
2962   sdata = 0;
2963   while (dataiterator_step(&di))
2964     {
2965       if (di.data - repo->repodata != repodataid)
2966         continue;
2967       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
2968         {
2969           dataiterator_entersub(&di);
2970           sdata = repo->repodata + stubdataids[i++];
2971           xkeyname = 0;
2972           continue;
2973         }
2974       switch (di.key->type)
2975         {
2976         case REPOKEY_TYPE_ID:
2977           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
2978           break;
2979         case REPOKEY_TYPE_CONSTANTID:
2980           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
2981           break;
2982         case REPOKEY_TYPE_STR:
2983           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
2984           break;
2985         case REPOKEY_TYPE_VOID:
2986           repodata_set_void(sdata, SOLVID_META, di.key->name);
2987           break;
2988         case REPOKEY_TYPE_NUM:
2989           repodata_set_num(sdata, SOLVID_META, di.key->name, di.kv.num);
2990           break;
2991         case REPOKEY_TYPE_MD5:
2992         case REPOKEY_TYPE_SHA1:
2993         case REPOKEY_TYPE_SHA256:
2994           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
2995           break;
2996         case REPOKEY_TYPE_IDARRAY:
2997           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
2998           if (di.key->name == REPOSITORY_KEYS)
2999             {
3000               Repokey xkey;
3001
3002               if (!xkeyname)
3003                 {
3004                   if (!di.kv.eof)
3005                     xkeyname = di.kv.id;
3006                   continue;
3007                 }
3008               xkey.name = xkeyname;
3009               xkey.type = di.kv.id;
3010               xkey.storage = KEY_STORAGE_INCORE;
3011               xkey.size = 0; 
3012               repodata_key2id(sdata, &xkey, 1);
3013               xkeyname = 0;
3014             }
3015         default:
3016           break;
3017         }
3018     }
3019   dataiterator_free(&di);
3020   for (i = 0; i < cnt; i++)
3021     repodata_internalize(repo->repodata + stubdataids[i]);
3022   solv_free(stubdataids);
3023 }
3024
3025 unsigned int
3026 repodata_memused(Repodata *data)
3027 {
3028   return data->incoredatalen + data->vincorelen;
3029 }
3030
3031 /*
3032 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
3033 */