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