support a special Datapos that describes the repodata meta section
[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 <= 0)
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   data->storestate++;
493   if (dp)
494     dp += off % REPOPAGE_BLOBSIZE;
495   return dp;
496 }
497
498 static inline unsigned char *
499 get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
500 {
501   unsigned char *dp = *dpp;
502
503   if (!dp)
504     return 0;
505   if (key->storage == KEY_STORAGE_INCORE)
506     {
507       if (advance)
508         *dpp = data_skip_key(data, dp, key);
509       return dp;
510     }
511   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
512     {
513       Id off, len;
514       dp = data_read_id(dp, &off);
515       dp = data_read_id(dp, &len);
516       if (advance)
517         *dpp = dp;
518       return get_vertical_data(data, key, off, len);
519     }
520   return 0;
521 }
522
523 static int
524 load_repodata(Repodata *data)
525 {
526   if (data->loadcallback)
527     {
528       data->loadcallback(data);
529       if (data->state == REPODATA_AVAILABLE)
530         return 1;
531     }
532   data->state = REPODATA_ERROR;
533   return 0;
534 }
535
536 static inline int
537 maybe_load_repodata(Repodata *data, Id keyname)
538 {
539   if (keyname && !repodata_precheck_keyname(data, keyname))
540     return 0;   /* do not bother... */
541   switch(data->state)
542     {
543     case REPODATA_STUB:
544       if (keyname)
545         {
546           int i;
547           for (i = 1; i < data->nkeys; i++)
548             if (keyname == data->keys[i].name)
549               break;
550           if (i == data->nkeys)
551             return 0;
552         }
553       return load_repodata(data);
554     case REPODATA_ERROR:
555       return 0;
556     case REPODATA_AVAILABLE:
557     case REPODATA_LOADING:
558       return 1;
559     default:
560       data->state = REPODATA_ERROR;
561       return 0;
562     }
563 }
564
565 static inline unsigned char *
566 solvid2data(Repodata *data, Id solvid, Id *schemap)
567 {
568   unsigned char *dp = data->incoredata;
569   if (!dp)
570     return 0;
571   if (solvid == SOLVID_META)
572     dp += 1;    /* offset of "meta" solvable */
573   else if (solvid == SOLVID_POS)
574     {
575       Pool *pool = data->repo->pool;
576       if (data->repo != pool->pos.repo)
577         return 0;
578       if (data != data->repo->repodata + pool->pos.repodataid)
579         return 0;
580       dp += pool->pos.dp;
581       if (pool->pos.dp != 1)
582         {
583           *schemap = pool->pos.schema;
584           return dp;
585         }
586     }
587   else
588     {
589       if (solvid < data->start || solvid >= data->end)
590         return 0;
591       dp += data->incoreoffset[solvid - data->start];
592     }
593   return data_read_id(dp, schemap);
594 }
595
596 /************************************************************************
597  * data lookup
598  */
599
600 static inline unsigned char *
601 find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
602 {
603   unsigned char *dp;
604   Id schema, *keyp, *kp;
605   Repokey *key;
606
607   if (!maybe_load_repodata(data, keyname))
608     return 0;
609   dp = solvid2data(data, solvid, &schema);
610   if (!dp)
611     return 0;
612   keyp = data->schemadata + data->schemata[schema];
613   for (kp = keyp; *kp; kp++)
614     if (data->keys[*kp].name == keyname)
615       break;
616   if (!*kp)
617     return 0;
618   *keypp = key = data->keys + *kp;
619   if (key->type == REPOKEY_TYPE_DELETED)
620     return 0;
621   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
622     return dp;  /* no need to forward... */
623   dp = forward_to_key(data, *kp, keyp, dp);
624   if (!dp)
625     return 0;
626   return get_data(data, key, &dp, 0);
627 }
628
629 Id
630 repodata_lookup_type(Repodata *data, Id solvid, Id keyname)
631 {
632   Id schema, *keyp, *kp;
633   if (!maybe_load_repodata(data, keyname))
634     return 0;
635   if (!solvid2data(data, solvid, &schema))
636     return 0;
637   keyp = data->schemadata + data->schemata[schema];
638   for (kp = keyp; *kp; kp++)
639     if (data->keys[*kp].name == keyname)
640       return data->keys[*kp].type;
641   return 0;
642 }
643
644 Id
645 repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
646 {
647   unsigned char *dp;
648   Repokey *key;
649   Id id;
650
651   dp = find_key_data(data, solvid, keyname, &key);
652   if (!dp)
653     return 0;
654   if (key->type == REPOKEY_TYPE_CONSTANTID)
655     return key->size;
656   if (key->type != REPOKEY_TYPE_ID)
657     return 0;
658   dp = data_read_id(dp, &id);
659   return id;
660 }
661
662 const char *
663 repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
664 {
665   unsigned char *dp;
666   Repokey *key;
667   Id id;
668
669   dp = find_key_data(data, solvid, keyname, &key);
670   if (!dp)
671     return 0;
672   if (key->type == REPOKEY_TYPE_STR)
673     return (const char *)dp;
674   if (key->type == REPOKEY_TYPE_CONSTANTID)
675     id = key->size;
676   else if (key->type == REPOKEY_TYPE_ID)
677     dp = data_read_id(dp, &id);
678   else
679     return 0;
680   if (data->localpool)
681     return stringpool_id2str(&data->spool, id);
682   return pool_id2str(data->repo->pool, id);
683 }
684
685 int
686 repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long *value)
687 {
688   unsigned char *dp;
689   Repokey *key;
690   unsigned int high, low;
691
692   *value = 0;
693   dp = find_key_data(data, solvid, keyname, &key);
694   if (!dp)
695     return 0;
696   switch (key->type)
697     {
698     case REPOKEY_TYPE_NUM:
699       data_read_num64(dp, &low, &high);
700       *value = (unsigned long long)high << 32 | low;
701       return 1;
702     case REPOKEY_TYPE_U32:
703       data_read_u32(dp, &low);
704       *value = low;
705       return 1;
706     case REPOKEY_TYPE_CONSTANT:
707       *value = key->size;
708       return 1;
709     default:
710       return 0;
711     }
712 }
713
714 int
715 repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
716 {
717   Id schema;
718   Id *keyp;
719   unsigned char *dp;
720
721   if (!maybe_load_repodata(data, keyname))
722     return 0;
723   dp = solvid2data(data, solvid, &schema);
724   if (!dp)
725     return 0;
726   /* can't use find_key_data as we need to test the type */
727   for (keyp = data->schemadata + data->schemata[schema]; *keyp; keyp++)
728     if (data->keys[*keyp].name == keyname && data->keys[*keyp].type == REPOKEY_TYPE_VOID)
729       return 1;
730   return 0;
731 }
732
733 const unsigned char *
734 repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
735 {
736   unsigned char *dp;
737   Repokey *key;
738
739   dp = find_key_data(data, solvid, keyname, &key);
740   if (!dp)
741     return 0;
742   if (!(key->type == REPOKEY_TYPE_MD5 || key->type == REPOKEY_TYPE_SHA1 || key->type == REPOKEY_TYPE_SHA256))
743     return 0;
744   *typep = key->type;
745   return dp;
746 }
747
748 int
749 repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
750 {
751   unsigned char *dp;
752   Repokey *key;
753   Id id;
754   int eof = 0;
755
756   queue_empty(q);
757   dp = find_key_data(data, solvid, keyname, &key);
758   if (!dp)
759     return 0;
760   if (key->type != REPOKEY_TYPE_IDARRAY && key->type != REPOKEY_TYPE_REL_IDARRAY)
761     return 0;
762   for (;;)
763     {
764       dp = data_read_ideof(dp, &id, &eof);
765       queue_push(q, id);
766       if (eof)
767         break;
768     }
769   return 1;
770 }
771
772 Id
773 repodata_globalize_id(Repodata *data, Id id, int create)
774 {
775   if (!id || !data || !data->localpool)
776     return id;
777   return pool_str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
778 }
779
780 Id
781 repodata_localize_id(Repodata *data, Id id, int create)
782 {
783   if (!id || !data || !data->localpool)
784     return id;
785   return stringpool_str2id(&data->spool, pool_id2str(data->repo->pool, id), create);
786 }
787
788 Id
789 repodata_translate_id(Repodata *data, Repodata *fromdata, Id id, int create)
790 {
791   if (!id || !data || !fromdata)
792     return id;
793   if (!data->localpool || !fromdata->localpool)
794     {
795       if (fromdata->localpool)
796         id = repodata_globalize_id(fromdata, id, create);
797       if (data->localpool)
798         id = repodata_localize_id(data, id, create);
799       return id;
800     }
801   /* localpool is set in both data and fromdata */
802   return stringpool_str2id(&data->spool, stringpool_id2str(&fromdata->spool, id), create);
803 }
804
805 Id
806 repodata_lookup_id_uninternalized(Repodata *data, Id solvid, Id keyname, Id voidid)
807 {
808   Id *ap;
809   if (!data->attrs)
810     return 0;
811   ap = data->attrs[solvid - data->start];
812   if (!ap)
813     return 0;
814   for (; *ap; ap += 2)
815     {
816       if (data->keys[*ap].name != keyname)
817         continue;
818       if (data->keys[*ap].type == REPOKEY_TYPE_VOID)
819         return voidid;
820       if (data->keys[*ap].type == REPOKEY_TYPE_ID)
821         return ap[1];
822       return 0;
823     }
824   return 0;
825 }
826
827
828 /************************************************************************
829  * data search
830  */
831
832
833 int
834 repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
835 {
836   switch (key->type)
837     {
838     case REPOKEY_TYPE_ID:
839     case REPOKEY_TYPE_CONSTANTID:
840     case REPOKEY_TYPE_IDARRAY:
841       if (data && data->localpool)
842         kv->str = stringpool_id2str(&data->spool, kv->id);
843       else
844         kv->str = pool_id2str(pool, kv->id);
845       if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE)
846         {
847           const char *s;
848           for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
849             ;
850           if (*s == ':' && s > kv->str)
851             kv->str = s + 1;
852         }
853       return 1;
854     case REPOKEY_TYPE_STR:
855       return 1;
856     case REPOKEY_TYPE_DIRSTRARRAY:
857       if (!(flags & SEARCH_FILES))
858         return 1;       /* match just the basename */
859       if (kv->num)
860         return 1;       /* already stringified */
861       /* Put the full filename into kv->str.  */
862       kv->str = repodata_dir2str(data, kv->id, kv->str);
863       kv->num = 1;      /* mark stringification */
864       return 1;
865     case REPOKEY_TYPE_MD5:
866     case REPOKEY_TYPE_SHA1:
867     case REPOKEY_TYPE_SHA256:
868       if (!(flags & SEARCH_CHECKSUMS))
869         return 0;       /* skip em */
870       if (kv->num)
871         return 1;       /* already stringified */
872       kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
873       kv->num = 1;      /* mark stringification */
874       return 1;
875     default:
876       return 0;
877     }
878 }
879
880
881 struct subschema_data {
882   Solvable *s;
883   void *cbdata;
884   KeyValue *parent;
885 };
886
887 /* search a specific repodata */
888 void
889 repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
890 {
891   Id schema;
892   Repokey *key;
893   Id keyid, *kp, *keyp;
894   unsigned char *dp, *ddp;
895   int onekey = 0;
896   int stop;
897   KeyValue kv;
898   Solvable *s;
899
900   if (!maybe_load_repodata(data, keyname))
901     return;
902   if (solvid == SOLVID_SUBSCHEMA)
903     {
904       struct subschema_data *subd = cbdata;
905       cbdata = subd->cbdata;
906       s = subd->s;
907       schema = subd->parent->id;
908       dp = (unsigned char *)subd->parent->str;
909       kv.parent = subd->parent;
910     }
911   else
912     {
913       schema = 0;
914       dp = solvid2data(data, solvid, &schema);
915       if (!dp)
916         return;
917       s = data->repo->pool->solvables + solvid;
918       kv.parent = 0;
919     }
920   keyp = data->schemadata + data->schemata[schema];
921   if (keyname)
922     {
923       /* search for a specific key */
924       for (kp = keyp; *kp; kp++)
925         if (data->keys[*kp].name == keyname)
926           break;
927       if (!*kp)
928         return;
929       dp = forward_to_key(data, *kp, keyp, dp);
930       if (!dp)
931         return;
932       keyp = kp;
933       onekey = 1;
934     }
935   while ((keyid = *keyp++) != 0)
936     {
937       stop = 0;
938       key = data->keys + keyid;
939       ddp = get_data(data, key, &dp, *keyp ? 1 : 0);
940
941       if (key->type == REPOKEY_TYPE_DELETED)
942         continue;
943       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
944         {
945           struct subschema_data subd;
946           int nentries;
947           Id schema = 0;
948
949           subd.cbdata = cbdata;
950           subd.s = s;
951           subd.parent = &kv;
952           ddp = data_read_id(ddp, &nentries);
953           kv.num = nentries;
954           kv.entry = 0;
955           kv.eof = 0;
956           while (ddp && nentries > 0)
957             {
958               if (!--nentries)
959                 kv.eof = 1;
960               if (key->type == REPOKEY_TYPE_FLEXARRAY || !kv.entry)
961                 ddp = data_read_id(ddp, &schema);
962               kv.id = schema;
963               kv.str = (char *)ddp;
964               stop = callback(cbdata, s, data, key, &kv);
965               if (stop > SEARCH_NEXT_KEY)
966                 return;
967               if (stop && stop != SEARCH_ENTERSUB)
968                 break;
969               if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
970                 repodata_search(data, SOLVID_SUBSCHEMA, 0, flags, callback, &subd);
971               ddp = data_skip_schema(data, ddp, schema);
972               kv.entry++;
973             }
974           if (!nentries && (flags & SEARCH_ARRAYSENTINEL) != 0)
975             {
976               /* sentinel */
977               kv.eof = 2;
978               kv.str = (char *)ddp;
979               stop = callback(cbdata, s, data, key, &kv);
980               if (stop > SEARCH_NEXT_KEY)
981                 return;
982             }
983           if (onekey)
984             return;
985           continue;
986         }
987       kv.entry = 0;
988       do
989         {
990           ddp = data_fetch(ddp, &kv, key);
991           if (!ddp)
992             break;
993           stop = callback(cbdata, s, data, key, &kv);
994           kv.entry++;
995         }
996       while (!kv.eof && !stop);
997       if (onekey || stop > SEARCH_NEXT_KEY)
998         return;
999     }
1000 }
1001
1002 void
1003 repodata_setpos_kv(Repodata *data, KeyValue *kv)
1004 {
1005   Pool *pool = data->repo->pool;
1006   if (!kv)
1007     pool_clear_pos(pool);
1008   else
1009     {
1010       pool->pos.repo = data->repo;
1011       pool->pos.repodataid = data - data->repo->repodata;
1012       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
1013       pool->pos.schema = kv->id;
1014     }
1015 }
1016
1017 /************************************************************************
1018  * data iterator functions
1019  */
1020
1021 static inline Id *
1022 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
1023 {
1024   kv->id = keyname;
1025   switch (keyname)
1026     {
1027     case SOLVABLE_NAME:
1028       kv->eof = 1;
1029       return &s->name;
1030     case SOLVABLE_ARCH:
1031       kv->eof = 1;
1032       return &s->arch;
1033     case SOLVABLE_EVR:
1034       kv->eof = 1;
1035       return &s->evr;
1036     case SOLVABLE_VENDOR:
1037       kv->eof = 1;
1038       return &s->vendor;
1039     case SOLVABLE_PROVIDES:
1040       kv->eof = 0;
1041       return s->provides ? s->repo->idarraydata + s->provides : 0;
1042     case SOLVABLE_OBSOLETES:
1043       kv->eof = 0;
1044       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
1045     case SOLVABLE_CONFLICTS:
1046       kv->eof = 0;
1047       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
1048     case SOLVABLE_REQUIRES:
1049       kv->eof = 0;
1050       return s->requires ? s->repo->idarraydata + s->requires : 0;
1051     case SOLVABLE_RECOMMENDS:
1052       kv->eof = 0;
1053       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
1054     case SOLVABLE_SUPPLEMENTS:
1055       kv->eof = 0;
1056       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
1057     case SOLVABLE_SUGGESTS:
1058       kv->eof = 0;
1059       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
1060     case SOLVABLE_ENHANCES:
1061       kv->eof = 0;
1062       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
1063     case RPM_RPMDBID:
1064       kv->eof = 1;
1065       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
1066     default:
1067       return 0;
1068     }
1069 }
1070
1071 int
1072 datamatcher_init(Datamatcher *ma, const char *match, int flags)
1073 {
1074   ma->match = match;
1075   ma->flags = flags;
1076   ma->error = 0;
1077   ma->matchdata = 0;
1078   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1079     {
1080       ma->matchdata = solv_calloc(1, sizeof(regex_t));
1081       ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
1082       if (ma->error)
1083         {
1084           solv_free(ma->matchdata);
1085           ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
1086         }
1087     }
1088   if ((flags & SEARCH_FILES) != 0 && match)
1089     {
1090       /* prepare basename check */
1091       if ((flags & SEARCH_STRINGMASK) == SEARCH_STRING || (flags & SEARCH_STRINGMASK) == SEARCH_STRINGEND)
1092         {
1093           const char *p = strrchr(match, '/');
1094           ma->matchdata = (void *)(p ? p + 1 : match);
1095         }
1096       else if ((flags & SEARCH_STRINGMASK) == SEARCH_GLOB)
1097         {
1098           const char *p;
1099           for (p = match + strlen(match) - 1; p >= match; p--)
1100             if (*p == '[' || *p == ']' || *p == '*' || *p == '?' || *p == '/')
1101               break;
1102           ma->matchdata = (void *)(p + 1);
1103         }
1104     }
1105   return ma->error;
1106 }
1107
1108 void
1109 datamatcher_free(Datamatcher *ma)
1110 {
1111   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
1112     {
1113       regfree(ma->matchdata);
1114       solv_free(ma->matchdata);
1115     }
1116   ma->matchdata = 0;
1117 }
1118
1119 int
1120 datamatcher_match(Datamatcher *ma, const char *str)
1121 {
1122   int l;
1123   switch ((ma->flags & SEARCH_STRINGMASK))
1124     {
1125     case SEARCH_SUBSTRING:
1126       if (ma->flags & SEARCH_NOCASE)
1127         return strcasestr(str, ma->match) != 0;
1128       else
1129         return strstr(str, ma->match) != 0;
1130     case SEARCH_STRING:
1131       if (ma->flags & SEARCH_NOCASE)
1132         return !strcasecmp(ma->match, str);
1133       else
1134         return !strcmp(ma->match, str);
1135     case SEARCH_STRINGSTART:
1136       if (ma->flags & SEARCH_NOCASE)
1137         return !strncasecmp(ma->match, str, strlen(ma->match));
1138       else
1139         return !strncmp(ma->match, str, strlen(ma->match));
1140     case SEARCH_STRINGEND:
1141       l = strlen(str) - strlen(ma->match);
1142       if (l < 0)
1143         return 0;
1144       if (ma->flags & SEARCH_NOCASE)
1145         return !strcasecmp(ma->match, str + l);
1146       else
1147         return !strcmp(ma->match, str + l);
1148     case SEARCH_GLOB:
1149       return !fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
1150     case SEARCH_REGEX:
1151       return !regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0);
1152     default:
1153       return 0;
1154     }
1155 }
1156
1157 /* check if the matcher can match the provides basename */
1158
1159 int
1160 datamatcher_checkbasename(Datamatcher *ma, const char *basename)
1161 {
1162   int l;
1163   const char *match = ma->matchdata;
1164   if (!match)
1165     return 1;
1166   switch (ma->flags & SEARCH_STRINGMASK)
1167     {
1168     case SEARCH_STRING:
1169       break;
1170     case SEARCH_STRINGEND:
1171       if (match != ma->match)
1172         break;          /* had slash, do exact match on basename */
1173       /* FALLTHROUGH */
1174     case SEARCH_GLOB:
1175       /* check if the basename ends with match */
1176       l = strlen(basename) - strlen(match);
1177       if (l < 0)
1178         return 0;
1179       basename += l;
1180       break;
1181     default:
1182       return 1; /* maybe matches */
1183     }
1184   if ((ma->flags & SEARCH_NOCASE) != 0)
1185     return !strcasecmp(match, basename);
1186   else
1187     return !strcmp(match, basename);
1188 }
1189
1190 int
1191 repodata_filelistfilter_matches(Repodata *data, const char *str)
1192 {
1193   /* '.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$' */
1194   /* for now hardcoded */
1195   if (strstr(str, "bin/"))
1196     return 1;
1197   if (!strncmp(str, "/etc/", 5))
1198     return 1;
1199   if (!strcmp(str, "/usr/lib/sendmail"))
1200     return 1;
1201   return 0;
1202 }
1203
1204
1205 enum {
1206   di_bye,
1207
1208   di_enterrepo,
1209   di_entersolvable,
1210   di_enterrepodata,
1211   di_enterschema,
1212   di_enterkey,
1213
1214   di_nextattr,
1215   di_nextkey,
1216   di_nextrepodata,
1217   di_nextsolvable,
1218   di_nextrepo,
1219
1220   di_enterarray,
1221   di_nextarrayelement,
1222
1223   di_entersub,
1224   di_leavesub,
1225
1226   di_nextsolvablekey,
1227   di_entersolvablekey,
1228   di_nextsolvableattr
1229 };
1230
1231 /* see dataiterator.h for documentation */
1232 int
1233 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
1234 {
1235   memset(di, 0, sizeof(*di));
1236   di->pool = pool;
1237   di->flags = flags & ~SEARCH_THISSOLVID;
1238   if (!pool || (repo && repo->pool != pool))
1239     {
1240       di->state = di_bye;
1241       return -1;
1242     }
1243   if (match)
1244     {
1245       int error;
1246       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1247         {
1248           di->state = di_bye;
1249           return error;
1250         }
1251     }
1252   di->keyname = keyname;
1253   di->keynames[0] = keyname;
1254   dataiterator_set_search(di, repo, p);
1255   return 0;
1256 }
1257
1258 void
1259 dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
1260 {
1261   *di = *from;
1262   if (di->dupstr)
1263     {
1264       if (di->dupstr == di->kv.str)
1265         {
1266           di->dupstr = solv_malloc(di->dupstrn);
1267           memcpy(di->dupstr, from->dupstr, di->dupstrn);
1268         }
1269       else
1270         {
1271           di->dupstr = 0;
1272           di->dupstrn = 0;
1273         }
1274     }
1275   memset(&di->matcher, 0, sizeof(di->matcher));
1276   if (from->matcher.match)
1277     datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
1278   if (di->nparents)
1279     {
1280       /* fix pointers */
1281       int i;
1282       for (i = 1; i < di->nparents; i++)
1283         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1284       di->kv.parent = &di->parents[di->nparents - 1].kv;
1285     }
1286 }
1287
1288 int
1289 dataiterator_set_match(Dataiterator *di, const char *match, int flags)
1290 {
1291   di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
1292   datamatcher_free(&di->matcher);
1293   memset(&di->matcher, 0, sizeof(di->matcher));
1294   if (match)
1295     {
1296       int error;
1297       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1298         {
1299           di->state = di_bye;
1300           return error;
1301         }
1302     }
1303   return 0;
1304 }
1305
1306 void
1307 dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
1308 {
1309   di->repo = repo;
1310   di->repoid = 0;
1311   di->flags &= ~SEARCH_THISSOLVID;
1312   di->nparents = 0;
1313   di->rootlevel = 0;
1314   di->repodataid = 1;
1315   if (!di->pool->urepos)
1316     {
1317       di->state = di_bye;
1318       return;
1319     }
1320   if (!repo)
1321     {
1322       di->repoid = 1;
1323       di->repo = di->pool->repos[di->repoid];
1324     }
1325   di->state = di_enterrepo;
1326   if (p)
1327     dataiterator_jump_to_solvid(di, p);
1328 }
1329
1330 void
1331 dataiterator_set_keyname(Dataiterator *di, Id keyname)
1332 {
1333   di->nkeynames = 0;
1334   di->keyname = keyname;
1335   di->keynames[0] = keyname;
1336 }
1337
1338 void
1339 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
1340 {
1341   int i;
1342
1343   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
1344     {
1345       di->state = di_bye;       /* sorry */
1346       return;
1347     }
1348   for (i = di->nkeynames + 1; i > 0; i--)
1349     di->keynames[i] = di->keynames[i - 1];
1350   di->keynames[0] = di->keyname = keyname;
1351   di->nkeynames++;
1352 }
1353
1354 void
1355 dataiterator_free(Dataiterator *di)
1356 {
1357   if (di->matcher.match)
1358     datamatcher_free(&di->matcher);
1359   if (di->dupstr)
1360     solv_free(di->dupstr);
1361 }
1362
1363 static inline unsigned char *
1364 dataiterator_find_keyname(Dataiterator *di, Id keyname)
1365 {
1366   Id *keyp = di->keyp;
1367   Repokey *keys = di->data->keys;
1368   unsigned char *dp;
1369
1370   for (keyp = di->keyp; *keyp; keyp++)
1371     if (keys[*keyp].name == keyname)
1372       break;
1373   if (!*keyp)
1374     return 0;
1375   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1376   if (!dp)
1377     return 0;
1378   di->keyp = keyp;
1379   return dp;
1380 }
1381
1382 static int
1383 dataiterator_filelistcheck(Dataiterator *di)
1384 {
1385   int j;
1386   int needcomplete = 0;
1387   Repodata *data = di->data;
1388
1389   if ((di->matcher.flags & SEARCH_COMPLETE_FILELIST) != 0)
1390     if (!di->matcher.match
1391        || ((di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_STRING
1392            && (di->matcher.flags & (SEARCH_STRINGMASK|SEARCH_NOCASE)) != SEARCH_GLOB)
1393        || !repodata_filelistfilter_matches(di->data, di->matcher.match))
1394       needcomplete = 1;
1395   if (data->state != REPODATA_AVAILABLE)
1396     return needcomplete ? 1 : 0;
1397   for (j = 1; j < data->nkeys; j++)
1398     if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
1399       break;
1400   return j == data->nkeys && !needcomplete ? 0 : 1;
1401 }
1402
1403 int
1404 dataiterator_step(Dataiterator *di)
1405 {
1406   Id schema;
1407
1408   if (di->state == di_nextattr && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET && di->vert_ddp && di->vert_storestate != di->data->storestate) {
1409     unsigned int ddpoff = di->ddp - di->vert_ddp;
1410     di->vert_off += ddpoff;
1411     di->vert_len -= ddpoff;
1412     di->ddp = di->vert_ddp = get_vertical_data(di->data, di->key, di->vert_off, di->vert_len);
1413     di->vert_storestate = di->data->storestate;
1414     if (!di->ddp)
1415       di->state = di_nextkey;
1416   }
1417   for (;;)
1418     {
1419       switch (di->state)
1420         {
1421         case di_enterrepo: di_enterrepo:
1422           if (!di->repo || (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS)))
1423             goto di_nextrepo;
1424           if (!(di->flags & SEARCH_THISSOLVID))
1425             {
1426               di->solvid = di->repo->start - 1; /* reset solvid iterator */
1427               goto di_nextsolvable;
1428             }
1429           /* FALLTHROUGH */
1430
1431         case di_entersolvable: di_entersolvable:
1432           if (di->repodataid)
1433             {
1434               di->repodataid = 1;       /* reset repodata iterator */
1435               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)
1436                 {
1437                   extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
1438
1439                   di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1440                   di->data = 0;
1441                   goto di_entersolvablekey;
1442                 }
1443             }
1444           /* FALLTHROUGH */
1445
1446         case di_enterrepodata: di_enterrepodata:
1447           if (di->repodataid)
1448             {
1449               if (di->repodataid >= di->repo->nrepodata)
1450                 goto di_nextsolvable;
1451               di->data = di->repo->repodata + di->repodataid;
1452             }
1453           if (di->repodataid && di->keyname == SOLVABLE_FILELIST && !dataiterator_filelistcheck(di))
1454             goto di_nextrepodata;
1455           if (!maybe_load_repodata(di->data, di->keyname))
1456             goto di_nextrepodata;
1457           di->dp = solvid2data(di->data, di->solvid, &schema);
1458           if (!di->dp)
1459             goto di_nextrepodata;
1460           if (di->solvid == SOLVID_POS)
1461             di->solvid = di->pool->pos.solvid;
1462           /* reset key iterator */
1463           di->keyp = di->data->schemadata + di->data->schemata[schema];
1464           /* FALLTHROUGH */
1465
1466         case di_enterschema: di_enterschema:
1467           if (di->keyname)
1468             di->dp = dataiterator_find_keyname(di, di->keyname);
1469           if (!di->dp || !*di->keyp)
1470             {
1471               if (di->kv.parent)
1472                 goto di_leavesub;
1473               goto di_nextrepodata;
1474             }
1475           /* FALLTHROUGH */
1476
1477         case di_enterkey: di_enterkey:
1478           di->kv.entry = -1;
1479           di->key = di->data->keys + *di->keyp;
1480           if (!di->dp)
1481             goto di_nextkey;
1482           /* this is get_data() modified to store vert_ data */
1483           if (di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1484             {
1485               Id off, len;
1486               di->dp = data_read_id(di->dp, &off);
1487               di->dp = data_read_id(di->dp, &len);
1488               di->vert_ddp = di->ddp = get_vertical_data(di->data, di->key, off, len);
1489               di->vert_off = off;
1490               di->vert_len = len;
1491               di->vert_storestate = di->data->storestate;
1492             }
1493           else if (di->key->storage == KEY_STORAGE_INCORE)
1494             {
1495               di->ddp = di->dp;
1496               if (di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0))
1497                 di->dp = data_skip_key(di->data, di->dp, di->key);
1498             }
1499           else
1500             di->ddp = 0;
1501           if (!di->ddp)
1502             goto di_nextkey;
1503           if (di->key->type == REPOKEY_TYPE_DELETED)
1504             goto di_nextkey;
1505           if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1506             goto di_enterarray;
1507           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1508             goto di_nextkey;
1509           /* FALLTHROUGH */
1510
1511         case di_nextattr:
1512           di->kv.entry++;
1513           di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1514           if (di->kv.eof)
1515             di->state = di_nextkey;
1516           else
1517             di->state = di_nextattr;
1518           break;
1519
1520         case di_nextkey: di_nextkey:
1521           if (!di->keyname && *++di->keyp)
1522             goto di_enterkey;
1523           if (di->kv.parent)
1524             goto di_leavesub;
1525           /* FALLTHROUGH */
1526
1527         case di_nextrepodata: di_nextrepodata:
1528           if (di->repodataid && ++di->repodataid < di->repo->nrepodata)
1529               goto di_enterrepodata;
1530           /* FALLTHROUGH */
1531
1532         case di_nextsolvable: di_nextsolvable:
1533           if (!(di->flags & SEARCH_THISSOLVID))
1534             {
1535               if (di->solvid < 0)
1536                 di->solvid = di->repo->start;
1537               else
1538                 di->solvid++;
1539               for (; di->solvid < di->repo->end; di->solvid++)
1540                 {
1541                   if (di->pool->solvables[di->solvid].repo == di->repo)
1542                     goto di_entersolvable;
1543                 }
1544             }
1545           /* FALLTHROUGH */
1546
1547         case di_nextrepo: di_nextrepo:
1548           if (di->repoid > 0)
1549             {
1550               di->repoid++;
1551               di->repodataid = 1;
1552               if (di->repoid < di->pool->nrepos)
1553                 {
1554                   di->repo = di->pool->repos[di->repoid];
1555                   goto di_enterrepo;
1556                 }
1557             }
1558         /* FALLTHROUGH */
1559
1560         case di_bye: di_bye:
1561           di->state = di_bye;
1562           return 0;
1563
1564         case di_enterarray: di_enterarray:
1565           if (di->key->name == REPOSITORY_SOLVABLES)
1566             goto di_nextkey;
1567           di->ddp = data_read_id(di->ddp, (Id *)&di->kv.num);
1568           di->kv.eof = 0;
1569           di->kv.entry = -1;
1570           /* FALLTHROUGH */
1571
1572         case di_nextarrayelement: di_nextarrayelement:
1573           di->kv.entry++;
1574           if (di->kv.entry)
1575             di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1576           if (di->kv.entry == di->kv.num)
1577             {
1578               if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1579                 goto di_nextkey;
1580               if (!(di->flags & SEARCH_ARRAYSENTINEL))
1581                 goto di_nextkey;
1582               di->kv.str = (char *)di->ddp;
1583               di->kv.eof = 2;
1584               di->state = di_nextkey;
1585               break;
1586             }
1587           if (di->kv.entry == di->kv.num - 1)
1588             di->kv.eof = 1;
1589           if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1590             di->ddp = data_read_id(di->ddp, &di->kv.id);
1591           di->kv.str = (char *)di->ddp;
1592           if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1593             goto di_entersub;
1594           if ((di->flags & SEARCH_SUB) != 0)
1595             di->state = di_entersub;
1596           else
1597             di->state = di_nextarrayelement;
1598           break;
1599
1600         case di_entersub: di_entersub:
1601           if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1602             goto di_nextarrayelement;   /* sorry, full */
1603           di->parents[di->nparents].kv = di->kv;
1604           di->parents[di->nparents].dp = di->dp;
1605           di->parents[di->nparents].keyp = di->keyp;
1606           di->dp = (unsigned char *)di->kv.str;
1607           di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1608           memset(&di->kv, 0, sizeof(di->kv));
1609           di->kv.parent = &di->parents[di->nparents].kv;
1610           di->nparents++;
1611           di->keyname = di->keynames[di->nparents - di->rootlevel];
1612           goto di_enterschema;
1613
1614         case di_leavesub: di_leavesub:
1615           if (di->nparents - 1 < di->rootlevel)
1616             goto di_bye;
1617           di->nparents--;
1618           di->dp = di->parents[di->nparents].dp;
1619           di->kv = di->parents[di->nparents].kv;
1620           di->keyp = di->parents[di->nparents].keyp;
1621           di->key = di->data->keys + *di->keyp;
1622           di->ddp = (unsigned char *)di->kv.str;
1623           di->keyname = di->keynames[di->nparents - di->rootlevel];
1624           goto di_nextarrayelement;
1625
1626         /* special solvable attr handling follows */
1627
1628         case di_nextsolvablekey: di_nextsolvablekey:
1629           if (di->keyname || di->key->name == RPM_RPMDBID)
1630             goto di_enterrepodata;
1631           di->key++;
1632           /* FALLTHROUGH */
1633
1634         case di_entersolvablekey: di_entersolvablekey:
1635           di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1636           if (!di->idp || !*di->idp)
1637             goto di_nextsolvablekey;
1638           if (di->kv.eof)
1639             {
1640               /* not an array */
1641               di->kv.id = *di->idp;
1642               di->kv.num = *di->idp;    /* for rpmdbid */
1643               di->kv.num2 = 0;          /* for rpmdbid */
1644               di->kv.entry = 0;
1645               di->state = di_nextsolvablekey;
1646               break;
1647             }
1648           di->kv.entry = -1;
1649           /* FALLTHROUGH */
1650
1651         case di_nextsolvableattr:
1652           di->state = di_nextsolvableattr;
1653           di->kv.id = *di->idp++;
1654           di->kv.entry++;
1655           if (!*di->idp)
1656             {
1657               di->kv.eof = 1;
1658               di->state = di_nextsolvablekey;
1659             }
1660           break;
1661
1662         }
1663
1664       if (di->matcher.match)
1665         {
1666           /* simple pre-check so that we don't need to stringify */
1667           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->matcher.flags & SEARCH_FILES) != 0)
1668             if (!datamatcher_checkbasename(&di->matcher, di->kv.str))
1669               continue;
1670           if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1671             {
1672               if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1673                 return 1;
1674               continue;
1675             }
1676           if (!datamatcher_match(&di->matcher, di->kv.str))
1677             continue;
1678         }
1679       else
1680         {
1681           if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->flags & SEARCH_FILES) != 0)
1682             repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags);
1683         }
1684       /* found something! */
1685       return 1;
1686     }
1687 }
1688
1689 void
1690 dataiterator_entersub(Dataiterator *di)
1691 {
1692   if (di->state == di_nextarrayelement)
1693     di->state = di_entersub;
1694 }
1695
1696 void
1697 dataiterator_setpos(Dataiterator *di)
1698 {
1699   if (di->kv.eof == 2)
1700     {
1701       pool_clear_pos(di->pool);
1702       return;
1703     }
1704   di->pool->pos.solvid = di->solvid;
1705   di->pool->pos.repo = di->repo;
1706   di->pool->pos.repodataid = di->data - di->repo->repodata;
1707   di->pool->pos.schema = di->kv.id;
1708   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
1709 }
1710
1711 void
1712 dataiterator_setpos_parent(Dataiterator *di)
1713 {
1714   if (!di->kv.parent || di->kv.parent->eof == 2)
1715     {
1716       pool_clear_pos(di->pool);
1717       return;
1718     }
1719   di->pool->pos.solvid = di->solvid;
1720   di->pool->pos.repo = di->repo;
1721   di->pool->pos.repodataid = di->data - di->repo->repodata;
1722   di->pool->pos.schema = di->kv.parent->id;
1723   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
1724 }
1725
1726 /* clones just the position, not the search keys/matcher */
1727 void
1728 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
1729 {
1730   di->state = from->state;
1731   di->flags &= ~SEARCH_THISSOLVID;
1732   di->flags |= (from->flags & SEARCH_THISSOLVID);
1733   di->repo = from->repo;
1734   di->data = from->data;
1735   di->dp = from->dp;
1736   di->ddp = from->ddp;
1737   di->idp = from->idp;
1738   di->keyp = from->keyp;
1739   di->key = from->key;
1740   di->kv = from->kv;
1741   di->repodataid = from->repodataid;
1742   di->solvid = from->solvid;
1743   di->repoid = from->repoid;
1744   di->rootlevel = from->rootlevel;
1745   memcpy(di->parents, from->parents, sizeof(from->parents));
1746   di->nparents = from->nparents;
1747   if (di->nparents)
1748     {
1749       int i;
1750       for (i = 1; i < di->nparents; i++)
1751         di->parents[i].kv.parent = &di->parents[i - 1].kv;
1752       di->kv.parent = &di->parents[di->nparents - 1].kv;
1753     }
1754   di->dupstr = 0;
1755   di->dupstrn = 0;
1756   if (from->dupstr && from->dupstr == from->kv.str)
1757     {
1758       di->dupstrn = from->dupstrn;
1759       di->dupstr = solv_malloc(from->dupstrn);
1760       memcpy(di->dupstr, from->dupstr, di->dupstrn);
1761     }
1762 }
1763
1764 void
1765 dataiterator_seek(Dataiterator *di, int whence)
1766 {
1767   if ((whence & DI_SEEK_STAY) != 0)
1768     di->rootlevel = di->nparents;
1769   switch (whence & ~DI_SEEK_STAY)
1770     {
1771     case DI_SEEK_CHILD:
1772       if (di->state != di_nextarrayelement)
1773         break;
1774       if ((whence & DI_SEEK_STAY) != 0)
1775         di->rootlevel = di->nparents + 1;       /* XXX: dangerous! */
1776       di->state = di_entersub;
1777       break;
1778     case DI_SEEK_PARENT:
1779       if (!di->nparents)
1780         {
1781           di->state = di_bye;
1782           break;
1783         }
1784       di->nparents--;
1785       if (di->rootlevel > di->nparents)
1786         di->rootlevel = di->nparents;
1787       di->dp = di->parents[di->nparents].dp;
1788       di->kv = di->parents[di->nparents].kv;
1789       di->keyp = di->parents[di->nparents].keyp;
1790       di->key = di->data->keys + *di->keyp;
1791       di->ddp = (unsigned char *)di->kv.str;
1792       di->keyname = di->keynames[di->nparents - di->rootlevel];
1793       di->state = di_nextarrayelement;
1794       break;
1795     case DI_SEEK_REWIND:
1796       if (!di->nparents)
1797         {
1798           di->state = di_bye;
1799           break;
1800         }
1801       di->dp = (unsigned char *)di->kv.parent->str;
1802       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
1803       di->state = di_enterschema;
1804       break;
1805     default:
1806       break;
1807     }
1808 }
1809
1810 void
1811 dataiterator_skip_attribute(Dataiterator *di)
1812 {
1813   if (di->state == di_nextsolvableattr)
1814     di->state = di_nextsolvablekey;
1815   else
1816     di->state = di_nextkey;
1817 }
1818
1819 void
1820 dataiterator_skip_solvable(Dataiterator *di)
1821 {
1822   di->nparents = 0;
1823   di->kv.parent = 0;
1824   di->rootlevel = 0;
1825   di->keyname = di->keynames[0];
1826   di->state = di_nextsolvable;
1827 }
1828
1829 void
1830 dataiterator_skip_repo(Dataiterator *di)
1831 {
1832   di->nparents = 0;
1833   di->kv.parent = 0;
1834   di->rootlevel = 0;
1835   di->keyname = di->keynames[0];
1836   di->state = di_nextrepo;
1837 }
1838
1839 void
1840 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
1841 {
1842   di->nparents = 0;
1843   di->kv.parent = 0;
1844   di->rootlevel = 0;
1845   di->keyname = di->keynames[0];
1846   if (solvid == SOLVID_POS)
1847     {
1848       di->repo = di->pool->pos.repo;
1849       if (!di->repo)
1850         {
1851           di->state = di_bye;
1852           return;
1853         }
1854       di->repoid = 0;
1855       di->data = di->repo->repodata + di->pool->pos.repodataid;
1856       di->repodataid = 0;
1857       di->solvid = solvid;
1858       di->state = di_enterrepo;
1859       di->flags |= SEARCH_THISSOLVID;
1860       return;
1861     }
1862   if (solvid > 0)
1863     {
1864       di->repo = di->pool->solvables[solvid].repo;
1865       di->repoid = 0;
1866     }
1867   else if (di->repoid > 0)
1868     {
1869       if (!di->pool->urepos)
1870         {
1871           di->state = di_bye;
1872           return;
1873         }
1874       di->repoid = 1;
1875       di->repo = di->pool->repos[di->repoid];
1876     }
1877   di->repodataid = 1;
1878   di->solvid = solvid;
1879   if (solvid)
1880     di->flags |= SEARCH_THISSOLVID;
1881   di->state = di_enterrepo;
1882 }
1883
1884 void
1885 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
1886 {
1887   di->nparents = 0;
1888   di->kv.parent = 0;
1889   di->rootlevel = 0;
1890   di->repo = repo;
1891   di->repoid = 0;       /* 0 means stay at repo */
1892   di->repodataid = 1;
1893   di->solvid = 0;
1894   di->flags &= ~SEARCH_THISSOLVID;
1895   di->state = di_enterrepo;
1896 }
1897
1898 int
1899 dataiterator_match(Dataiterator *di, Datamatcher *ma)
1900 {
1901   if (!repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags))
1902     return 0;
1903   if (!ma)
1904     return 1;
1905   return datamatcher_match(ma, di->kv.str);
1906 }
1907
1908 void
1909 dataiterator_strdup(Dataiterator *di)
1910 {
1911   int l = -1;
1912
1913   if (!di->kv.str || di->kv.str == di->dupstr)
1914     return;
1915   switch (di->key->type)
1916     {
1917     case REPOKEY_TYPE_MD5:
1918     case REPOKEY_TYPE_SHA1:
1919     case REPOKEY_TYPE_SHA256:
1920     case REPOKEY_TYPE_DIRSTRARRAY:
1921       if (di->kv.num)   /* was it stringified into tmp space? */
1922         l = strlen(di->kv.str) + 1;
1923       break;
1924     default:
1925       break;
1926     }
1927   if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1928     {
1929       switch (di->key->type)
1930         {
1931         case REPOKEY_TYPE_STR:
1932         case REPOKEY_TYPE_DIRSTRARRAY:
1933           l = strlen(di->kv.str) + 1;
1934           break;
1935         case REPOKEY_TYPE_MD5:
1936           l = SIZEOF_MD5;
1937           break;
1938         case REPOKEY_TYPE_SHA1:
1939           l = SIZEOF_SHA1;
1940           break;
1941         case REPOKEY_TYPE_SHA256:
1942           l = SIZEOF_SHA256;
1943           break;
1944         case REPOKEY_TYPE_BINARY:
1945           l = di->kv.num;
1946           break;
1947         }
1948     }
1949   if (l >= 0)
1950     {
1951       if (!di->dupstrn || di->dupstrn < l)
1952         {
1953           di->dupstrn = l + 16;
1954           di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
1955         }
1956       if (l)
1957         memcpy(di->dupstr, di->kv.str, l);
1958       di->kv.str = di->dupstr;
1959     }
1960 }
1961
1962 /************************************************************************
1963  * data modify functions
1964  */
1965
1966 /* extend repodata so that it includes solvables p */
1967 void
1968 repodata_extend(Repodata *data, Id p)
1969 {
1970   if (data->start == data->end)
1971     data->start = data->end = p;
1972   if (p >= data->end)
1973     {
1974       int old = data->end - data->start;
1975       int new = p - data->end + 1;
1976       if (data->attrs)
1977         {
1978           data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
1979           memset(data->attrs + old, 0, new * sizeof(Id *));
1980         }
1981       data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
1982       memset(data->incoreoffset + old, 0, new * sizeof(Id));
1983       data->end = p + 1;
1984     }
1985   if (p < data->start)
1986     {
1987       int old = data->end - data->start;
1988       int new = data->start - p;
1989       if (data->attrs)
1990         {
1991           data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
1992           memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
1993           memset(data->attrs, 0, new * sizeof(Id *));
1994         }
1995       data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
1996       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
1997       memset(data->incoreoffset, 0, new * sizeof(Id));
1998       data->start = p;
1999     }
2000 }
2001
2002 /* shrink end of repodata */
2003 void
2004 repodata_shrink(Repodata *data, int end)
2005 {
2006   int i;
2007
2008   if (data->end <= end)
2009     return;
2010   if (data->start >= end)
2011     {
2012       if (data->attrs)
2013         {
2014           for (i = 0; i < data->end - data->start; i++)
2015             solv_free(data->attrs[i]);
2016           data->attrs = solv_free(data->attrs);
2017         }
2018       data->incoreoffset = solv_free(data->incoreoffset);
2019       data->start = data->end = 0;
2020       return;
2021     }
2022   if (data->attrs)
2023     {
2024       for (i = end; i < data->end; i++)
2025         solv_free(data->attrs[i - data->start]);
2026       data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
2027     }
2028   if (data->incoreoffset)
2029     data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
2030   data->end = end;
2031 }
2032
2033 /* extend repodata so that it includes solvables from start to start + num - 1 */
2034 void
2035 repodata_extend_block(Repodata *data, Id start, Id num)
2036 {
2037   if (!num)
2038     return;
2039   if (!data->incoreoffset)
2040     {
2041       data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
2042       data->start = start;
2043       data->end = start + num;
2044       return;
2045     }
2046   repodata_extend(data, start);
2047   if (num > 1)
2048     repodata_extend(data, start + num - 1);
2049 }
2050
2051 /**********************************************************************/
2052
2053
2054 #define REPODATA_ATTRS_BLOCK 31
2055 #define REPODATA_ATTRDATA_BLOCK 1023
2056 #define REPODATA_ATTRIDDATA_BLOCK 63
2057 #define REPODATA_ATTRNUM64DATA_BLOCK 15
2058
2059
2060 Id
2061 repodata_new_handle(Repodata *data)
2062 {
2063   if (!data->nxattrs)
2064     {
2065       data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2066       data->nxattrs = 2;        /* -1: SOLVID_META */
2067     }
2068   data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
2069   data->xattrs[data->nxattrs] = 0;
2070   return -(data->nxattrs++);
2071 }
2072
2073 static inline Id **
2074 repodata_get_attrp(Repodata *data, Id handle)
2075 {
2076   if (handle < 0)
2077     {
2078       if (handle == SOLVID_META && !data->xattrs)
2079         {
2080           data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2081           data->nxattrs = 2;
2082         }
2083       return data->xattrs - handle;
2084     }
2085   if (handle < data->start || handle >= data->end)
2086     repodata_extend(data, handle);
2087   if (!data->attrs)
2088     data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
2089   return data->attrs + (handle - data->start);
2090 }
2091
2092 static void
2093 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
2094 {
2095   Id *pp;
2096   Id *ap, **app;
2097   int i;
2098
2099   app = repodata_get_attrp(data, handle);
2100   ap = *app;
2101   i = 0;
2102   if (ap)
2103     {
2104       /* Determine equality based on the name only, allows us to change
2105          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
2106       for (pp = ap; *pp; pp += 2)
2107         if (data->keys[*pp].name == data->keys[keyid].name)
2108           break;
2109       if (*pp)
2110         {
2111           if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
2112             {
2113               pp[0] = keyid;
2114               pp[1] = val;
2115             }
2116           return;
2117         }
2118       i = pp - ap;
2119     }
2120   ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
2121   *app = ap;
2122   pp = ap + i;
2123   *pp++ = keyid;
2124   *pp++ = val;
2125   *pp = 0;
2126 }
2127
2128
2129 static void
2130 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
2131 {
2132   Id keyid;
2133
2134   keyid = repodata_key2id(data, key, 1);
2135   repodata_insert_keyid(data, solvid, keyid, val, 1);
2136 }
2137
2138 void
2139 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
2140 {
2141   Repokey key;
2142   key.name = keyname;
2143   key.type = REPOKEY_TYPE_ID;
2144   key.size = 0;
2145   key.storage = KEY_STORAGE_INCORE;
2146   repodata_set(data, solvid, &key, id);
2147 }
2148
2149 void
2150 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
2151 {
2152   Repokey key;
2153   key.name = keyname;
2154   key.type = REPOKEY_TYPE_NUM;
2155   key.size = 0;
2156   key.storage = KEY_STORAGE_INCORE;
2157   if (num >= 0x80000000)
2158     {
2159       data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
2160       data->attrnum64data[data->attrnum64datalen] = num;
2161       num = 0x80000000 | data->attrnum64datalen++;
2162     }
2163   repodata_set(data, solvid, &key, (Id)num);
2164 }
2165
2166 void
2167 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
2168 {
2169   Repokey key;
2170   Id id;
2171   if (data->localpool)
2172     id = stringpool_str2id(&data->spool, str, 1);
2173   else
2174     id = pool_str2id(data->repo->pool, str, 1);
2175   key.name = keyname;
2176   key.type = REPOKEY_TYPE_ID;
2177   key.size = 0;
2178   key.storage = KEY_STORAGE_INCORE;
2179   repodata_set(data, solvid, &key, id);
2180 }
2181
2182 void
2183 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
2184 {
2185   Repokey key;
2186   key.name = keyname;
2187   key.type = REPOKEY_TYPE_CONSTANT;
2188   key.size = constant;
2189   key.storage = KEY_STORAGE_INCORE;
2190   repodata_set(data, solvid, &key, 0);
2191 }
2192
2193 void
2194 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
2195 {
2196   Repokey key;
2197   key.name = keyname;
2198   key.type = REPOKEY_TYPE_CONSTANTID;
2199   key.size = id;
2200   key.storage = KEY_STORAGE_INCORE;
2201   repodata_set(data, solvid, &key, 0);
2202 }
2203
2204 void
2205 repodata_set_void(Repodata *data, Id solvid, Id keyname)
2206 {
2207   Repokey key;
2208   key.name = keyname;
2209   key.type = REPOKEY_TYPE_VOID;
2210   key.size = 0;
2211   key.storage = KEY_STORAGE_INCORE;
2212   repodata_set(data, solvid, &key, 0);
2213 }
2214
2215 void
2216 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
2217 {
2218   Repokey key;
2219   int l;
2220
2221   l = strlen(str) + 1;
2222   key.name = keyname;
2223   key.type = REPOKEY_TYPE_STR;
2224   key.size = 0;
2225   key.storage = KEY_STORAGE_INCORE;
2226   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2227   memcpy(data->attrdata + data->attrdatalen, str, l);
2228   repodata_set(data, solvid, &key, data->attrdatalen);
2229   data->attrdatalen += l;
2230 }
2231
2232 void
2233 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
2234 {
2235   Repokey key;
2236   unsigned char *dp;
2237
2238   if (len < 0)
2239     return;
2240   key.name = keyname;
2241   key.type = REPOKEY_TYPE_BINARY;
2242   key.size = 0;
2243   key.storage = KEY_STORAGE_INCORE;
2244   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
2245   dp = data->attrdata + data->attrdatalen;
2246   if (len >= (1 << 14))
2247     {
2248       if (len >= (1 << 28))
2249         *dp++ = (len >> 28) | 128;
2250       if (len >= (1 << 21))
2251         *dp++ = (len >> 21) | 128;
2252       *dp++ = (len >> 14) | 128;
2253     }
2254   if (len >= (1 << 7))
2255     *dp++ = (len >> 7) | 128;
2256   *dp++ = len & 127;
2257   if (len)
2258     memcpy(dp, buf, len);
2259   repodata_set(data, solvid, &key, data->attrdatalen);
2260   data->attrdatalen = dp + len - data->attrdata;
2261 }
2262
2263 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
2264  * so that the caller can append entrysize new elements plus the termination zero there */
2265 static void
2266 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2267 {
2268   int oldsize;
2269   Id *ida, *pp, **ppp;
2270
2271   /* check if it is the same as last time, this speeds things up a lot */
2272   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2273     {
2274       /* great! just append the new data */
2275       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2276       data->attriddatalen--;    /* overwrite terminating 0  */
2277       data->lastdatalen += entrysize;
2278       return;
2279     }
2280
2281   ppp = repodata_get_attrp(data, handle);
2282   pp = *ppp;
2283   if (pp)
2284     {
2285       for (; *pp; pp += 2)
2286         if (data->keys[*pp].name == keyname)
2287           break;
2288     }
2289   if (!pp || !*pp || data->keys[*pp].type != keytype)
2290     {
2291       /* not found. allocate new key */
2292       Repokey key;
2293       Id keyid;
2294       key.name = keyname;
2295       key.type = keytype;
2296       key.size = 0;
2297       key.storage = KEY_STORAGE_INCORE;
2298       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2299       keyid = repodata_key2id(data, &key, 1);
2300       repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
2301       data->lasthandle = handle;
2302       data->lastkey = keyid;
2303       data->lastdatalen = data->attriddatalen + entrysize + 1;
2304       return;
2305     }
2306   oldsize = 0;
2307   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2308     oldsize += entrysize;
2309   if (ida + 1 == data->attriddata + data->attriddatalen)
2310     {
2311       /* this was the last entry, just append it */
2312       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2313       data->attriddatalen--;    /* overwrite terminating 0  */
2314     }
2315   else
2316     {
2317       /* too bad. move to back. */
2318       data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2319       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2320       pp[1] = data->attriddatalen;
2321       data->attriddatalen += oldsize;
2322     }
2323   data->lasthandle = handle;
2324   data->lastkey = *pp;
2325   data->lastdatalen = data->attriddatalen + entrysize + 1;
2326 }
2327
2328 void
2329 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2330                       const unsigned char *str)
2331 {
2332   Repokey key;
2333   int l;
2334
2335   if (!(l = solv_chksum_len(type)))
2336     return;
2337   key.name = keyname;
2338   key.type = type;
2339   key.size = 0;
2340   key.storage = KEY_STORAGE_INCORE;
2341   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2342   memcpy(data->attrdata + data->attrdatalen, str, l);
2343   repodata_set(data, solvid, &key, data->attrdatalen);
2344   data->attrdatalen += l;
2345 }
2346
2347 void
2348 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2349                       const char *str)
2350 {
2351   unsigned char buf[64];
2352   int l;
2353
2354   if (!(l = solv_chksum_len(type)))
2355     return;
2356   if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
2357     return;
2358   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2359 }
2360
2361 const char *
2362 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2363 {
2364   int l;
2365
2366   if (!(l = solv_chksum_len(type)))
2367     return "";
2368   return pool_bin2hex(data->repo->pool, buf, l);
2369 }
2370
2371 /* rpm filenames don't contain the epoch, so strip it */
2372 static inline const char *
2373 evrid2vrstr(Pool *pool, Id evrid)
2374 {
2375   const char *p, *evr = pool_id2str(pool, evrid);
2376   if (!evr)
2377     return evr;
2378   for (p = evr; *p >= '0' && *p <= '9'; p++)
2379     ;
2380   return p != evr && *p == ':' && p[1] ? p + 1 : evr;
2381 }
2382
2383 static inline void
2384 repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2385 {
2386   Id id;
2387   if (data->localpool)
2388     id = stringpool_strn2id(&data->spool, str, l, 1);
2389   else
2390     id = pool_strn2id(data->repo->pool, str, l, 1);
2391   repodata_set_id(data, solvid, keyname, id);
2392 }
2393
2394 static inline void
2395 repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2396 {
2397   if (!str[l])
2398     repodata_set_str(data, solvid, keyname, str);
2399   else
2400     {
2401       char *s = solv_strdup(str);
2402       s[l] = 0;
2403       repodata_set_str(data, solvid, keyname, s);
2404       free(s);
2405     }
2406 }
2407
2408 void
2409 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2410 {
2411   Pool *pool = data->repo->pool;
2412   Solvable *s;
2413   const char *str, *fp;
2414   int l = 0;
2415
2416   if (medianr)
2417     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2418   if (!dir)
2419     {
2420       if ((dir = strrchr(file, '/')) != 0)
2421         {
2422           l = dir - file;
2423           dir = file;
2424           file = dir + l + 1;
2425           if (!l)
2426             l++;
2427         }
2428     }
2429   else
2430     l = strlen(dir);
2431   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2432     {
2433       dir += 2;
2434       l -= 2;
2435     }
2436   if (l == 1 && dir[0] == '.')
2437     l = 0;
2438   s = pool->solvables + solvid;
2439   if (dir && l)
2440     {
2441       str = pool_id2str(pool, s->arch);
2442       if (!strncmp(dir, str, l) && !str[l])
2443         repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2444       else
2445         repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
2446     }
2447   fp = file;
2448   str = pool_id2str(pool, s->name);
2449   l = strlen(str);
2450   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2451     {
2452       fp += l + 1;
2453       str = evrid2vrstr(pool, s->evr);
2454       l = strlen(str);
2455       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2456         {
2457           fp += l + 1;
2458           str = pool_id2str(pool, s->arch);
2459           l = strlen(str);
2460           if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2461             {
2462               repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2463               return;
2464             }
2465         }
2466     }
2467   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2468 }
2469
2470 /* XXX: medianr is currently not stored */
2471 void
2472 repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
2473 {
2474   int l = 0;
2475   const char *evr, *suf, *s;
2476
2477   if (!dir)
2478     {
2479       if ((dir = strrchr(file, '/')) != 0)
2480         {
2481           l = dir - file;
2482           dir = file;
2483           file = dir + l + 1;
2484           if (!l)
2485             l++;
2486         }
2487     }
2488   else
2489     l = strlen(dir);
2490   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2491     {
2492       dir += 2;
2493       l -= 2;
2494     }
2495   if (l == 1 && dir[0] == '.')
2496     l = 0;
2497   if (dir && l)
2498     repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
2499   evr = strchr(file, '-');
2500   if (evr)
2501     {
2502       for (s = evr - 1; s > file; s--)
2503         if (*s == '-')
2504           {
2505             evr = s;
2506             break;
2507           }
2508     }
2509   suf = strrchr(file, '.');
2510   if (suf)
2511     {
2512       for (s = suf - 1; s > file; s--)
2513         if (*s == '.')
2514           {
2515             suf = s;
2516             break;
2517           }
2518       if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
2519         {
2520           /* We accept one more item as suffix.  */
2521           for (s = suf - 1; s > file; s--)
2522             if (*s == '.')
2523               {
2524                 suf = s;
2525                 break;
2526               }
2527         }
2528     }
2529   if (!evr)
2530     suf = 0;
2531   if (suf && evr && suf < evr)
2532     suf = 0;
2533   repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
2534   if (evr)
2535     repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
2536   if (suf)
2537     repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
2538 }
2539
2540 void
2541 repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
2542 {
2543   Pool *pool = data->repo->pool;
2544   Solvable *s = pool->solvables + solvid;
2545   const char *p, *sevr, *sarch, *name, *evr;
2546
2547   p = strrchr(sourcepkg, '.');
2548   if (!p || strcmp(p, ".rpm") != 0)
2549     {
2550       if (*sourcepkg)
2551         repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
2552       return;
2553     }
2554   p--;
2555   while (p > sourcepkg && *p != '.')
2556     p--;
2557   if (*p != '.' || p == sourcepkg)
2558     return;
2559   sarch = p-- + 1;
2560   while (p > sourcepkg && *p != '-')
2561     p--;
2562   if (*p != '-' || p == sourcepkg)
2563     return;
2564   p--;
2565   while (p > sourcepkg && *p != '-')
2566     p--;
2567   if (*p != '-' || p == sourcepkg)
2568     return;
2569   sevr = p + 1;
2570   pool = s->repo->pool;
2571
2572   name = pool_id2str(pool, s->name);
2573   if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
2574     repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
2575   else
2576     repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
2577
2578   evr = evrid2vrstr(pool, s->evr);
2579   if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
2580     repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
2581   else
2582     repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
2583
2584   if (!strcmp(sarch, "src.rpm"))
2585     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
2586   else if (!strcmp(sarch, "nosrc.rpm"))
2587     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
2588   else
2589     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
2590 }
2591
2592 void
2593 repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
2594 {
2595   Repokey key;
2596   int i;
2597
2598   key.name = keyname;
2599   key.type = REPOKEY_TYPE_IDARRAY;
2600   key.size = 0;
2601   key.storage = KEY_STORAGE_INCORE;
2602   repodata_set(data, solvid, &key, data->attriddatalen);
2603   data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2604   for (i = 0; i < q->count; i++)
2605     data->attriddata[data->attriddatalen++] = q->elements[i];
2606   data->attriddata[data->attriddatalen++] = 0;
2607 }
2608
2609 void
2610 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2611 {
2612   assert(dir);
2613 #if 0
2614 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2615 #endif
2616   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2617   data->attriddata[data->attriddatalen++] = dir;
2618   data->attriddata[data->attriddatalen++] = num;
2619   data->attriddata[data->attriddatalen++] = num2;
2620   data->attriddata[data->attriddatalen++] = 0;
2621 }
2622
2623 void
2624 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2625 {
2626   Id stroff;
2627   int l;
2628
2629   assert(dir);
2630   l = strlen(str) + 1;
2631   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2632   memcpy(data->attrdata + data->attrdatalen, str, l);
2633   stroff = data->attrdatalen;
2634   data->attrdatalen += l;
2635
2636 #if 0
2637 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2638 #endif
2639   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2640   data->attriddata[data->attriddatalen++] = dir;
2641   data->attriddata[data->attriddatalen++] = stroff;
2642   data->attriddata[data->attriddatalen++] = 0;
2643 }
2644
2645 void
2646 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2647 {
2648 #if 0
2649 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2650 #endif
2651   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2652   data->attriddata[data->attriddatalen++] = id;
2653   data->attriddata[data->attriddatalen++] = 0;
2654 }
2655
2656 void
2657 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2658                            const char *str)
2659 {
2660   Id id;
2661   if (data->localpool)
2662     id = stringpool_str2id(&data->spool, str, 1);
2663   else
2664     id = pool_str2id(data->repo->pool, str, 1);
2665   repodata_add_idarray(data, solvid, keyname, id);
2666 }
2667
2668 void
2669 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2670 {
2671   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2672   data->attriddata[data->attriddatalen++] = ghandle;
2673   data->attriddata[data->attriddatalen++] = 0;
2674 }
2675
2676 void
2677 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id ghandle)
2678 {
2679   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2680   data->attriddata[data->attriddatalen++] = ghandle;
2681   data->attriddata[data->attriddatalen++] = 0;
2682 }
2683
2684 void
2685 repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
2686 {
2687   Id *pp, *ap, **app;
2688   app = repodata_get_attrp(data, solvid);
2689   ap = *app;
2690   if (!ap)
2691     return;
2692   for (; *ap; ap += 2)
2693     if (data->keys[*ap].name == keyname)
2694       break;
2695   if (!*ap)
2696     return;
2697   pp = ap;
2698   ap += 2;
2699   for (; *ap; ap += 2)
2700     {
2701       if (data->keys[*ap].name == keyname)
2702         continue;
2703       *pp++ = ap[0];
2704       *pp++ = ap[1];
2705     }
2706   *pp = 0;
2707 }
2708
2709 /* XXX: does not work correctly, needs fix in iterators! */
2710 void
2711 repodata_unset(Repodata *data, Id solvid, Id keyname)
2712 {
2713   Repokey key;
2714   key.name = keyname;
2715   key.type = REPOKEY_TYPE_DELETED;
2716   key.size = 0;
2717   key.storage = KEY_STORAGE_INCORE;
2718   repodata_set(data, solvid, &key, 0);
2719 }
2720
2721 /* add all (uninternalized) attrs from src to dest */
2722 void
2723 repodata_merge_attrs(Repodata *data, Id dest, Id src)
2724 {
2725   Id *keyp;
2726   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2727     return;
2728   for (; *keyp; keyp += 2)
2729     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
2730 }
2731
2732 /* add some (uninternalized) attrs from src to dest */
2733 void
2734 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
2735 {
2736   Id *keyp;
2737   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
2738     return;
2739   for (; *keyp; keyp += 2)
2740     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
2741       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
2742 }
2743
2744 /* swap (uninternalized) attrs from src and dest */
2745 void
2746 repodata_swap_attrs(Repodata *data, Id dest, Id src)
2747 {
2748   Id *tmpattrs;
2749   if (!data->attrs || dest == src)
2750     return;
2751   tmpattrs = data->attrs[dest - data->start];
2752   data->attrs[dest - data->start] = data->attrs[src - data->start];
2753   data->attrs[src - data->start] = tmpattrs;
2754 }
2755
2756
2757 /**********************************************************************/
2758
2759 /* TODO: unify with repo_write and repo_solv! */
2760
2761 #define EXTDATA_BLOCK 1023
2762
2763 struct extdata {
2764   unsigned char *buf;
2765   int len;
2766 };
2767
2768 static void
2769 data_addid(struct extdata *xd, Id sx)
2770 {
2771   unsigned int x = (unsigned int)sx;
2772   unsigned char *dp;
2773
2774   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2775   dp = xd->buf + xd->len;
2776
2777   if (x >= (1 << 14))
2778     {
2779       if (x >= (1 << 28))
2780         *dp++ = (x >> 28) | 128;
2781       if (x >= (1 << 21))
2782         *dp++ = (x >> 21) | 128;
2783       *dp++ = (x >> 14) | 128;
2784     }
2785   if (x >= (1 << 7))
2786     *dp++ = (x >> 7) | 128;
2787   *dp++ = x & 127;
2788   xd->len = dp - xd->buf;
2789 }
2790
2791 static void
2792 data_addid64(struct extdata *xd, unsigned long long x)
2793 {
2794   if (x >= 0x100000000)
2795     {
2796       if ((x >> 35) != 0)
2797         {
2798           data_addid(xd, (Id)(x >> 35));
2799           xd->buf[xd->len - 1] |= 128;
2800         }
2801       data_addid(xd, (Id)((unsigned int)x | 0x80000000));
2802       xd->buf[xd->len - 5] = (x >> 28) | 128;
2803     }
2804   else
2805     data_addid(xd, (Id)x);
2806 }
2807
2808 static void
2809 data_addideof(struct extdata *xd, Id sx, int eof)
2810 {
2811   unsigned int x = (unsigned int)sx;
2812   unsigned char *dp;
2813
2814   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
2815   dp = xd->buf + xd->len;
2816
2817   if (x >= (1 << 13))
2818     {
2819       if (x >= (1 << 27))
2820         *dp++ = (x >> 27) | 128;
2821       if (x >= (1 << 20))
2822         *dp++ = (x >> 20) | 128;
2823       *dp++ = (x >> 13) | 128;
2824     }
2825   if (x >= (1 << 6))
2826     *dp++ = (x >> 6) | 128;
2827   *dp++ = eof ? (x & 63) : (x & 63) | 64;
2828   xd->len = dp - xd->buf;
2829 }
2830
2831 static void
2832 data_addblob(struct extdata *xd, unsigned char *blob, int len)
2833 {
2834   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
2835   memcpy(xd->buf + xd->len, blob, len);
2836   xd->len += len;
2837 }
2838
2839 /*********************************/
2840
2841 /* internalalize some key into incore/vincore data */
2842
2843 static void
2844 repodata_serialize_key(Repodata *data, struct extdata *newincore,
2845                        struct extdata *newvincore,
2846                        Id *schema,
2847                        Repokey *key, Id val)
2848 {
2849   Id *ida;
2850   struct extdata *xd;
2851   unsigned int oldvincorelen = 0;
2852   Id schemaid, *sp;
2853
2854   xd = newincore;
2855   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2856     {
2857       xd = newvincore;
2858       oldvincorelen = xd->len;
2859     }
2860   switch (key->type)
2861     {
2862     case REPOKEY_TYPE_VOID:
2863     case REPOKEY_TYPE_CONSTANT:
2864     case REPOKEY_TYPE_CONSTANTID:
2865       break;
2866     case REPOKEY_TYPE_STR:
2867       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
2868       break;
2869     case REPOKEY_TYPE_MD5:
2870       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
2871       break;
2872     case REPOKEY_TYPE_SHA1:
2873       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
2874       break;
2875     case REPOKEY_TYPE_SHA256:
2876       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
2877       break;
2878     case REPOKEY_TYPE_NUM:
2879       if (val & 0x80000000)
2880         {
2881           data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
2882           break;
2883         }
2884       /* FALLTHROUGH */
2885     case REPOKEY_TYPE_ID:
2886     case REPOKEY_TYPE_DIR:
2887       data_addid(xd, val);
2888       break;
2889     case REPOKEY_TYPE_BINARY:
2890       {
2891         Id len;
2892         unsigned char *dp = data_read_id(data->attrdata + val, &len);
2893         dp += (unsigned int)len;
2894         data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
2895       }
2896       break;
2897     case REPOKEY_TYPE_IDARRAY:
2898       for (ida = data->attriddata + val; *ida; ida++)
2899         data_addideof(xd, ida[0], ida[1] ? 0 : 1);
2900       break;
2901     case REPOKEY_TYPE_DIRNUMNUMARRAY:
2902       for (ida = data->attriddata + val; *ida; ida += 3)
2903         {
2904           data_addid(xd, ida[0]);
2905           data_addid(xd, ida[1]);
2906           data_addideof(xd, ida[2], ida[3] ? 0 : 1);
2907         }
2908       break;
2909     case REPOKEY_TYPE_DIRSTRARRAY:
2910       for (ida = data->attriddata + val; *ida; ida += 2)
2911         {
2912           data_addideof(xd, ida[0], ida[2] ? 0 : 1);
2913           data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
2914         }
2915       break;
2916     case REPOKEY_TYPE_FIXARRAY:
2917       {
2918         int num = 0;
2919         schemaid = 0;
2920         for (ida = data->attriddata + val; *ida; ida++)
2921           {
2922             Id *kp;
2923             sp = schema;
2924             kp = data->xattrs[-*ida];
2925             if (!kp)
2926               continue;
2927             num++;
2928             for (;*kp; kp += 2)
2929               *sp++ = *kp;
2930             *sp = 0;
2931             if (!schemaid)
2932               schemaid = repodata_schema2id(data, schema, 1);
2933             else if (schemaid != repodata_schema2id(data, schema, 0))
2934               {
2935                 pool_debug(data->repo->pool, SOLV_FATAL, "fixarray substructs with different schemas\n");
2936                 exit(1);
2937               }
2938           }
2939         if (!num)
2940           break;
2941         data_addid(xd, num);
2942         data_addid(xd, schemaid);
2943         for (ida = data->attriddata + val; *ida; ida++)
2944           {
2945             Id *kp = data->xattrs[-*ida];
2946             if (!kp)
2947               continue;
2948             for (;*kp; kp += 2)
2949               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
2950           }
2951         break;
2952       }
2953     case REPOKEY_TYPE_FLEXARRAY:
2954       {
2955         int num = 0;
2956         for (ida = data->attriddata + val; *ida; ida++)
2957           num++;
2958         data_addid(xd, num);
2959         for (ida = data->attriddata + val; *ida; ida++)
2960           {
2961             Id *kp = data->xattrs[-*ida];
2962             if (!kp)
2963               {
2964                 data_addid(xd, 0);      /* XXX */
2965                 continue;
2966               }
2967             sp = schema;
2968             for (;*kp; kp += 2)
2969               *sp++ = *kp;
2970             *sp = 0;
2971             schemaid = repodata_schema2id(data, schema, 1);
2972             data_addid(xd, schemaid);
2973             kp = data->xattrs[-*ida];
2974             for (;*kp; kp += 2)
2975               repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
2976           }
2977         break;
2978       }
2979     default:
2980       pool_debug(data->repo->pool, SOLV_FATAL, "don't know how to handle type %d\n", key->type);
2981       exit(1);
2982     }
2983   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2984     {
2985       /* put offset/len in incore */
2986       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
2987       oldvincorelen = xd->len - oldvincorelen;
2988       data_addid(newincore, oldvincorelen);
2989     }
2990 }
2991
2992 void
2993 repodata_internalize(Repodata *data)
2994 {
2995   Repokey *key, solvkey;
2996   Id entry, nentry;
2997   Id schemaid, *schema, *sp, oldschema, *keyp, *keypstart, *seen;
2998   unsigned char *dp, *ndp;
2999   int newschema, oldcount;
3000   struct extdata newincore;
3001   struct extdata newvincore;
3002   Id solvkeyid;
3003
3004   if (!data->attrs && !data->xattrs)
3005     return;
3006
3007   newvincore.buf = data->vincore;
3008   newvincore.len = data->vincorelen;
3009
3010   /* find the solvables key, create if needed */
3011   memset(&solvkey, 0, sizeof(solvkey));
3012   solvkey.name = REPOSITORY_SOLVABLES;
3013   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
3014   solvkey.size = 0;
3015   solvkey.storage = KEY_STORAGE_INCORE;
3016   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
3017
3018   schema = solv_malloc2(data->nkeys, sizeof(Id));
3019   seen = solv_malloc2(data->nkeys, sizeof(Id));
3020
3021   /* Merge the data already existing (in data->schemata, ->incoredata and
3022      friends) with the new attributes in data->attrs[].  */
3023   nentry = data->end - data->start;
3024   memset(&newincore, 0, sizeof(newincore));
3025   data_addid(&newincore, 0);    /* start data at offset 1 */
3026
3027   data->mainschema = 0;
3028   data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
3029
3030   /* join entry data */
3031   /* we start with the meta data, entry -1 */
3032   for (entry = -1; entry < nentry; entry++)
3033     {
3034       memset(seen, 0, data->nkeys * sizeof(Id));
3035       oldschema = 0;
3036       dp = data->incoredata;
3037       if (dp)
3038         {
3039           dp += entry >= 0 ? data->incoreoffset[entry] : 1;
3040           dp = data_read_id(dp, &oldschema);
3041         }
3042 #if 0
3043 fprintf(stderr, "oldschema %d\n", oldschema);
3044 fprintf(stderr, "schemata %d\n", data->schemata[oldschema]);
3045 fprintf(stderr, "schemadata %p\n", data->schemadata);
3046 #endif
3047       /* seen: -1: old data  0: skipped  >0: id + 1 */
3048       newschema = 0;
3049       oldcount = 0;
3050       sp = schema;
3051       for (keyp = data->schemadata + data->schemata[oldschema]; *keyp; keyp++)
3052         {
3053           if (seen[*keyp])
3054             {
3055               pool_debug(data->repo->pool, SOLV_FATAL, "Inconsistent old data (key occured twice).\n");
3056               exit(1);
3057             }
3058           seen[*keyp] = -1;
3059           *sp++ = *keyp;
3060           oldcount++;
3061         }
3062       if (entry >= 0)
3063         keyp = data->attrs ? data->attrs[entry] : 0;
3064       else
3065         {
3066           /* strip solvables key */
3067           *sp = 0;
3068           for (sp = keyp = schema; *sp; sp++)
3069             if (*sp != solvkeyid)
3070               *keyp++ = *sp;
3071             else
3072               oldcount--;
3073           sp = keyp;
3074           seen[solvkeyid] = 0;
3075           keyp = data->xattrs ? data->xattrs[1] : 0;
3076         }
3077       if (keyp)
3078         for (; *keyp; keyp += 2)
3079           {
3080             if (!seen[*keyp])
3081               {
3082                 newschema = 1;
3083                 *sp++ = *keyp;
3084               }
3085             seen[*keyp] = keyp[1] + 1;
3086           }
3087       if (entry < 0 && data->end != data->start)
3088         {
3089           *sp++ = solvkeyid;
3090           newschema = 1;
3091         }
3092       *sp = 0;
3093       if (newschema)
3094         /* Ideally we'd like to sort the new schema here, to ensure
3095            schema equality independend of the ordering.  We can't do that
3096            yet.  For once see below (old ids need to come before new ids).
3097            An additional difficulty is that we also need to move
3098            the values with the keys.  */
3099         schemaid = repodata_schema2id(data, schema, 1);
3100       else
3101         schemaid = oldschema;
3102
3103
3104       /* Now create data blob.  We walk through the (possibly new) schema
3105          and either copy over old data, or insert the new.  */
3106       /* XXX Here we rely on the fact that the (new) schema has the form
3107          o1 o2 o3 o4 ... | n1 n2 n3 ...
3108          (oX being the old keyids (possibly overwritten), and nX being
3109           the new keyids).  This rules out sorting the keyids in order
3110          to ensure a small schema count.  */
3111       if (entry >= 0)
3112         data->incoreoffset[entry] = newincore.len;
3113       data_addid(&newincore, schemaid);
3114       if (entry == -1)
3115         {
3116           data->mainschema = schemaid;
3117           data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
3118         }
3119       keypstart = data->schemadata + data->schemata[schemaid];
3120       for (keyp = keypstart; *keyp; keyp++)
3121         {
3122           if (entry == -1)
3123             data->mainschemaoffsets[keyp - keypstart] = newincore.len;
3124           if (*keyp == solvkeyid)
3125             {
3126               /* add flexarray entry count */
3127               data_addid(&newincore, data->end - data->start);
3128               break;
3129             }
3130           key = data->keys + *keyp;
3131 #if 0
3132           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));
3133 #endif
3134           ndp = dp;
3135           if (oldcount)
3136             {
3137               /* Skip the data associated with this old key.  */
3138               if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3139                 {
3140                   ndp = data_skip(dp, REPOKEY_TYPE_ID);
3141                   ndp = data_skip(ndp, REPOKEY_TYPE_ID);
3142                 }
3143               else if (key->storage == KEY_STORAGE_INCORE)
3144                 ndp = data_skip_key(data, dp, key);
3145               oldcount--;
3146             }
3147           if (seen[*keyp] == -1)
3148             {
3149               /* If this key was an old one _and_ was not overwritten with
3150                  a different value copy over the old value (we skipped it
3151                  above).  */
3152               if (dp != ndp)
3153                 data_addblob(&newincore, dp, ndp - dp);
3154               seen[*keyp] = 0;
3155             }
3156           else if (seen[*keyp])
3157             {
3158               /* Otherwise we have a new value.  Parse it into the internal
3159                  form.  */
3160               repodata_serialize_key(data, &newincore, &newvincore,
3161                                      schema, key, seen[*keyp] - 1);
3162             }
3163           dp = ndp;
3164         }
3165       if (entry >= 0 && data->attrs && data->attrs[entry])
3166         data->attrs[entry] = solv_free(data->attrs[entry]);
3167     }
3168   /* free all xattrs */
3169   for (entry = 0; entry < data->nxattrs; entry++)
3170     if (data->xattrs[entry])
3171       solv_free(data->xattrs[entry]);
3172   data->xattrs = solv_free(data->xattrs);
3173   data->nxattrs = 0;
3174
3175   data->lasthandle = 0;
3176   data->lastkey = 0;
3177   data->lastdatalen = 0;
3178   solv_free(schema);
3179   solv_free(seen);
3180   repodata_free_schemahash(data);
3181
3182   solv_free(data->incoredata);
3183   data->incoredata = newincore.buf;
3184   data->incoredatalen = newincore.len;
3185   data->incoredatafree = 0;
3186
3187   solv_free(data->vincore);
3188   data->vincore = newvincore.buf;
3189   data->vincorelen = newvincore.len;
3190
3191   data->attrs = solv_free(data->attrs);
3192   data->attrdata = solv_free(data->attrdata);
3193   data->attriddata = solv_free(data->attriddata);
3194   data->attrnum64data = solv_free(data->attrnum64data);
3195   data->attrdatalen = 0;
3196   data->attriddatalen = 0;
3197   data->attrnum64datalen = 0;
3198 }
3199
3200 void
3201 repodata_disable_paging(Repodata *data)
3202 {
3203   if (maybe_load_repodata(data, 0))
3204     {
3205       repopagestore_disable_paging(&data->store);
3206       data->storestate++;
3207     }
3208 }
3209
3210 static void
3211 repodata_load_stub(Repodata *data)
3212 {
3213   Repo *repo = data->repo;
3214   Pool *pool = repo->pool;
3215   int r, i;
3216   struct _Pool_tmpspace oldtmpspace;
3217
3218   if (!pool->loadcallback)
3219     {
3220       data->state = REPODATA_ERROR;
3221       return;
3222     }
3223   data->state = REPODATA_LOADING;
3224
3225   /* save tmp space */
3226   oldtmpspace = pool->tmpspace;
3227   memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
3228
3229   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
3230
3231   /* restore tmp space */
3232   for (i = 0; i < POOL_TMPSPACEBUF; i++)
3233     solv_free(pool->tmpspace.buf[i]);
3234   pool->tmpspace = oldtmpspace;
3235
3236   data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
3237 }
3238
3239 void
3240 repodata_create_stubs(Repodata *data)
3241 {
3242   Repo *repo = data->repo;
3243   Pool *pool = repo->pool;
3244   Repodata *sdata;
3245   int *stubdataids;
3246   Dataiterator di;
3247   Id xkeyname = 0;
3248   int i, cnt = 0;
3249   int repodataid;
3250   int datastart, dataend;
3251
3252   repodataid = data - repo->repodata;
3253   datastart = data->start;
3254   dataend = data->end;
3255   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3256   while (dataiterator_step(&di))
3257     {
3258       if (di.data - repo->repodata != repodataid)
3259         continue;
3260       cnt++;
3261     }
3262   dataiterator_free(&di);
3263   if (!cnt)
3264     return;
3265   stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
3266   for (i = 0; i < cnt; i++)
3267     {
3268       sdata = repo_add_repodata(repo, 0);
3269       if (dataend > datastart)
3270         repodata_extend_block(sdata, datastart, dataend - datastart);
3271       stubdataids[i] = sdata - repo->repodata;
3272       sdata->state = REPODATA_STUB;
3273       sdata->loadcallback = repodata_load_stub;
3274     }
3275   i = 0;
3276   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3277   sdata = 0;
3278   while (dataiterator_step(&di))
3279     {
3280       if (di.data - repo->repodata != repodataid)
3281         continue;
3282       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
3283         {
3284           dataiterator_entersub(&di);
3285           sdata = repo->repodata + stubdataids[i++];
3286           xkeyname = 0;
3287           continue;
3288         }
3289       switch (di.key->type)
3290         {
3291         case REPOKEY_TYPE_ID:
3292           repodata_set_id(sdata, SOLVID_META, di.key->name, di.kv.id);
3293           break;
3294         case REPOKEY_TYPE_CONSTANTID:
3295           repodata_set_constantid(sdata, SOLVID_META, di.key->name, di.kv.id);
3296           break;
3297         case REPOKEY_TYPE_STR:
3298           repodata_set_str(sdata, SOLVID_META, di.key->name, di.kv.str);
3299           break;
3300         case REPOKEY_TYPE_VOID:
3301           repodata_set_void(sdata, SOLVID_META, di.key->name);
3302           break;
3303         case REPOKEY_TYPE_NUM:
3304           repodata_set_num(sdata, SOLVID_META, di.key->name, SOLV_KV_NUM64(&di.kv));
3305           break;
3306         case REPOKEY_TYPE_MD5:
3307         case REPOKEY_TYPE_SHA1:
3308         case REPOKEY_TYPE_SHA256:
3309           repodata_set_bin_checksum(sdata, SOLVID_META, di.key->name, di.key->type, (const unsigned char *)di.kv.str);
3310           break;
3311         case REPOKEY_TYPE_IDARRAY:
3312           repodata_add_idarray(sdata, SOLVID_META, di.key->name, di.kv.id);
3313           if (di.key->name == REPOSITORY_KEYS)
3314             {
3315               Repokey xkey;
3316
3317               if (!xkeyname)
3318                 {
3319                   if (!di.kv.eof)
3320                     xkeyname = di.kv.id;
3321                   continue;
3322                 }
3323               xkey.name = xkeyname;
3324               xkey.type = di.kv.id;
3325               xkey.storage = KEY_STORAGE_INCORE;
3326               xkey.size = 0;
3327               repodata_key2id(sdata, &xkey, 1);
3328               xkeyname = 0;
3329             }
3330         default:
3331           break;
3332         }
3333     }
3334   dataiterator_free(&di);
3335   for (i = 0; i < cnt; i++)
3336     repodata_internalize(repo->repodata + stubdataids[i]);
3337   solv_free(stubdataids);
3338 }
3339
3340 unsigned int
3341 repodata_memused(Repodata *data)
3342 {
3343   return data->incoredatalen + data->vincorelen;
3344 }
3345
3346 /*
3347 vim:cinoptions={.5s,g0,p5,t0,(0,^-0.5s,n-0.5s:tw=78:cindent:sw=4:
3348 */