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